Skill: 12266601032/validation-rule-management (version 1.0.0)

--- name: validation-rule-management description: 管理校验规则、规则组和校验场景的全流程操作。支持通过统一 CLI 工具快速执行 API 调用，自动处理参数解析、配置加载和错误提示。使用当用户需要进行校验规则管理、规则组维护、校验场景配置、启停操作或相关查询时，即使用户只说"帮我创建一条规则"或"查一下场景列表"也应触发。 --- # 校验规则管理 ## 核心概念 ``` 校验场景 (ValidateScene) └─ 1:N 校验规则组 (ValidateRuleGroup) └─ 1:N 校验规则 (ValidateRule) ``` **状态**：`draft`（草稿）/ `enabled`（启用）/ `disabled`（禁用） --- ## 初始化（首次使用必做） 在使用任何命令前，**先检查 CLI 是否就绪**： ```bash npx vr help 2>/dev/null || (cd <skill目录> && npm install) ``` 如果 `npx vr help` 无输出或报错，说明还未初始化，执行： ```bash cd /path/to/validation-rule-management npm install ``` 完成后即可在任意目录通过 `npx --prefix /path/to/validation-rule-management vr <action>` 调用，或直接在 skill 目录下用 `npx vr <action>`。 --- ## 配置 **方式 1 — 环境变量**（推荐，适合脚本/CI） ```bash export VALIDATE_BASE_URL=http://global-masterdata-http.default.yf-bw-test-2.test.51baiwang.com export VALIDATE_TOKEN=your-token-here ``` **方式 2 — config.json**（放在项目根目录或 skill 目录） ```json { "baseUrl": "http://global-masterdata-http.default.yf-bw-test-2.test.51baiwang.com", "token": "your-token-here" } ``` --- ## 命令速查 > 所有命令统一格式：`npx vr <action> [--key value ...]` > 值含空格时 shell 自动处理，含 `=` 等特殊字符时用 `--json '{...}'` ### 规则组 ```bash npx vr create-rule-group --groupName "基础信息校验组" --orderNum 1 [--description "描述"] npx vr query-rule-groups [--groupName "关键字"] [--pageNum 1] [--pageSize 10] npx vr update-rule-group --id 1 --groupName "新名称" --orderNum 2 npx vr delete-rule-group --id 1 ``` ### 规则 ```bash npx vr create-rule --groupId 1 --objectId Invoice --countryCode CN \ --ruleCode CN-SELLER-001 --ruleName "卖方名称必填" \ --ruleType required [--fieldKey sellerName] [--errorMessage "{field}不能为空"] npx vr query-rules [--groupId 1] [--status enabled] [--countryCode CN] [--pageSize 20] npx vr enable-rule --id 42 --status enabled|disabled npx vr delete-rule --id 42 ``` ### 场景 ```bash npx vr create-scene --sceneCode CN_VAT_SPECIAL --sceneName "中国增值税专票场景" \ [--errorStrategy stop_on_error|continue_all] npx vr query-scenes [--sceneCode CN_VAT] [--status enabled] [--pageSize 10] npx vr enable-scene --id 10 --status enabled|disabled npx vr delete-scene --id 10 ``` --- ## 目录结构 ``` validation-rule-management/ ├── SKILL.md ├── package.json # 定义 bin: { "vr": "./cli.js" } ├── cli.js # 入口：解析 action → 路由到对应命令模块 ├── commands/ # 按业务域划分的命令模块 │ ├── scenes.js # 场景操作 │ ├── rule-groups.js # 规则组操作 │ └── rules.js # 规则操作 ├── lib/ # 基础设施 │ ├── api-client.js # HTTP 客户端 + 配置加载 │ ├── arg-parser.js # 命令行参数解析 │ └── utils.js # 公共工具函数 └── references/ # 参考文档（按需读取） ├── data-model.md # 完整 DTO 定义 + API 端点汇总 └── examples.md # 典型使用示例 ``` 查阅字段定义或 API 路径时，读取 `references/data-model.md`。 --- ## 常见问题 | 错误信息 | 原因 | 解决 | |----------|------|------| | `npx vr` 无响应 | 未执行 `npm install` | 在 skill 目录执行 `npm install` | | 未找到配置 | 没有环境变量或 config.json | 设置环境变量或创建配置文件 | | 缺少必填参数 | 参数不完整 | 运行 `npx vr help` 查看必填项 | | 业务错误 | token 失效或权限不足 | 刷新 token，确认 appId 正确 |

path: .gitignore
content: package-lock.json

path: cli.js
content: #!/usr/bin/env node
/**
 * 校验规则管理 CLI - 路由入口
 *
 * 用法（在 skill 目录下执行一次 npm install，之后）：
 *   npx vr <action> [--key value ...]
 *
 * 示例：
 *   npx vr create-rule --groupId 1 --ruleName "名称校验" --ruleCode CN-001 --objectId Invoice --countryCode CN --ruleType required
 *   npx vr query-scenes --status enabled
 *   npx vr enable-rule --id 42 --status disabled
 *   npx vr help
 */

const { parseArgs } = require('./lib/arg-parser');
const scenes     = require('./commands/scenes');
const ruleGroups = require('./commands/rule-groups');
const rules      = require('./commands/rules');

// 所有 action → handler 的统一路由表
const router = {
  ...scenes,
  ...ruleGroups,
  ...rules,
};

(async () => {
  const { action, params } = parseArgs(process.argv);

  if (!action || action === 'help' || action === '--help' || action === '-h') {
    printHelp();
    process.exit(0);
  }

  const handler = router[action];
  if (!handler) {
    console.error(`❌ 未知操作：${action}`);
    console.error(`   运行 npx vr help 查看全部可用操作`);
    process.exit(1);
  }

  try {
    await handler(params);
  } catch (e) {
    console.error(`\n❌ 执行失败：${e.message}`);
    process.exit(1);
  }
})();

function printHelp() {
  const domains = [
    { label: '规则组', cmds: Object.keys(ruleGroups) },
    { label: '规则',   cmds: Object.keys(rules) },
    { label: '场景',   cmds: Object.keys(scenes) },
  ];

  console.log('\n校验规则管理 CLI\n');
  console.log('用法：npx vr <action> [--key value ...]\n');
  console.log('可用操作：');
  for (const { label, cmds } of domains) {
    console.log(`  [${label}]  ${cmds.join('  ')}`);
  }
  console.log('\n参数格式：');
  console.log('  --key value         标准格式（推荐）');
  console.log('  --key=value         等号格式');
  console.log("  --json '{...}'      整体传入 JSON（含特殊字符时使用）");
  console.log('\n配置（任选其一）：');
  console.log('  环境变量：VALIDATE_BASE_URL=http://... VALIDATE_TOKEN=xxx npx vr ...');
  console.log('  config.json：{ "baseUrl": "...", "token": "...", "appId": "..." }');
  console.log();
}


path: package.json
content: {
  "name": "validate-rule-cli",
  "version": "1.0.0",
  "description": "校验规则管理 CLI",
  "license": "MIT-0",
  "bin": {
    "vr": "./cli.js"
  },
  "engines": {
    "node": ">=14"
  }
}


path: references/data-model.md
content: # 校验规则管理 - 数据模型参考

## DTO 定义

### ValidateRuleDTO
校验规则DTO

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| id | integer(int64) | 否 | 规则ID，更新时必填 | 1 |
| groupId | integer(int64) | 是 | 所属规则组ID | 1 |
| objectId | string | 是 | 校验对象 | Invoice |
| countryCode | string | 是 | 国家/地区代码，ISO 3166 | CN |
| ruleCode | string | 是 | 规则编码 | CN-SELLER-001 |
| ruleName | string | 是 | 规则名称 | 卖方名称必填校验 |
| description | string | 否 | 规则说明 | 校验卖方名称不能为空 |
| preconditions | string | 否 | 条件表达式 | countryCode == 'CN' |
| fieldKey | string | 否 | 字段key，非字段级校验不填 | sellerName |
| ruleType | string | 是 | 校验类型 | required |
| errorLevel | string | 是 | 错误级别：error/warn/info | error |
| errorCode | string | 否 | 对外暴露的错误码 | E10001 |
| errorMessage | string | 是 | 错误信息模板，支持{field}占位符 | {field}不能为空 |
| executionOrder | integer(int32) | 是 | 同规则组下规则的执行顺序 | 1 |
| status | string | 是 | 状态：draft/enabled/disabled | enabled |
| effectiveStart | string(date-time) | 否 | 生效日期 | 2024-01-01 |
| effectiveEnd | string(date-time) | 否 | 失效日期，NULL表示永久有效 | 2099-12-31 |
| extensions | object | 否 | 扩展属性 | {} |

---

### ValidateRuleGroupDTO
校验规则组DTO

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| id | integer(int64) | 否 | 规则组ID，更新时必填 | 1 |
| parentId | integer(int64) | 否 | 父级ID | 0 |
| tenantId | integer(int64) | 否 | 所属租户 | 1 |
| groupName | string | 是 | 规则组名称 | 基础信息校验组 |
| description | string | 否 | 规则组说明 | 用于校验发票基础信息 |
| orderNum | integer(int32) | 是 | 顺序号 | 1 |

---

### ValidateSceneDTO
校验场景DTO

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| id | integer(int64) | 否 | 场景ID，更新时必填 | 1 |
| sceneCode | string | 是 | 场景编码 | CN_VAT_SPECIAL |
| sceneName | string | 是 | 场景名称 | 中国增值税专票场景 |
| description | string | 否 | 场景说明 | 用于校验中国增值税专用发票 |
| status | string | 是 | 状态：draft/enabled/disabled | enabled |
| errorStrategy | string | 是 | 异常策略 | stop_on_error |
| extensions | object | 否 | 扩展属性 | {} |
| rules | array | 否 | 关联规则列表 | [] |

**errorStrategy 枚举值**:
- `stop_on_error`: 异常就结束
- `continue_all`: 执行所有

---

## QueryRequest 定义

### ValidateRuleQueryRequest
查询校验规则请求

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| id | integer(int64) | 否 | 规则ID | 1 |
| groupId | integer(int64) | 否 | 所属规则组ID | 1 |
| objectId | string | 否 | 校验对象 | Invoice |
| countryCode | string | 否 | 国家/地区代码 | CN |
| ruleCode | string | 否 | 规则编码 | CN-SELLER-001 |
| ruleName | string | 否 | 规则名称 | 卖方名称必填校验 |
| fieldKey | string | 否 | 字段key | sellerName |
| ruleType | string | 否 | 校验类型 | required |
| status | string | 否 | 状态 | enabled |
| pageNum | integer(int32) | 是 | 当前页码 | 1 |
| pageSize | integer(int32) | 是 | 每页条数 | 10 |

---

### ValidateRuleGroupQueryRequest
查询校验规则组请求

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| id | integer(int64) | 否 | 规则组ID | 1 |
| parentId | integer(int64) | 否 | 父级ID | 0 |
| groupName | string | 否 | 规则组名称 | 基础信息校验组 |
| pageNum | integer(int32) | 是 | 当前页码 | 1 |
| pageSize | integer(int32) | 是 | 每页条数 | 10 |

---

### ValidateSceneQueryRequest
查询校验场景请求

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| id | integer(int64) | 否 | 场景ID | 1 |
| sceneCode | string | 否 | 场景编码 | CN_VAT_SPECIAL |
| sceneName | string | 否 | 场景名称 | 中国增值税专票场景 |
| status | string | 否 | 状态 | enabled |
| pageNum | integer(int32) | 是 | 当前页码 | 1 |
| pageSize | integer(int32) | 是 | 每页条数 | 10 |

---

## 其他 DTO

### EntityPrimaryKeyDTO
实体主键DTO（用于详情查询、删除、启停）

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | integer(int64) | 是 | 实体ID |

---

### SaveGroupOrdersRequest
保存组顺序请求

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| groupOrders | array | 是 | 组顺序列表 |

### GroupOrderDTO
组顺序DTO

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| id | integer(int64) | 是 | 规则组ID | 1 |
| orderNum | integer(int32) | 是 | 顺序号 | 1 |

---

### ValidateEntitySchemasRequest
获取校验对象参数结构请求

| 字段 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| countryCode | string | 是 | 国家/地区代码 | CN |

---

## 响应结构

### BWJsonResultDto<T>
通用响应结构

| 字段 | 类型 | 说明 |
|------|------|------|
| requestId | string | 请求ID |
| success | boolean | 是否成功 |
| message | string | 消息 |
| errorCode | string | 错误码 |
| errorMsg | string | 错误信息 |
| total | integer | 总记录数（分页查询时） |
| data | T / array | 数据 |
| extInfo | object | 扩展信息 |
| kind | integer | 类型 |

---

## API 端点汇总

### 校验规则管理
| 接口 | 方法 | 说明 |
|------|------|------|
| /admin/validateRule/create | POST | 新增校验规则 |
| /admin/validateRule/update | POST | 更新校验规则 |
| /admin/validateRule/delete | POST | 删除校验规则 |
| /admin/validateRule/enable | POST | 启用校验规则 |
| /admin/validateRule/disable | POST | 禁用校验规则 |
| /admin/validateRule/queryPageList | POST | 分页查询校验规则 |
| /admin/validateRule/queryDetail | POST | 根据ID查询校验规则 |
| /admin/validateRule/validateEntitySchemas | POST | 获取校验对象参数结构 |

### 校验规则组管理
| 接口 | 方法 | 说明 |
|------|------|------|
| /admin/validateRuleGroup/create | POST | 新增校验规则组 |
| /admin/validateRuleGroup/update | POST | 更新校验规则组 |
| /admin/validateRuleGroup/delete | POST | 删除校验规则组 |
| /admin/validateRuleGroup/saveGroupOrders | POST | 更新校验规则组顺序 |
| /admin/validateRuleGroup/queryPageList | POST | 分页查询校验规则组 |
| /admin/validateRuleGroup/queryDetail | POST | 根据ID查询校验规则组 |

### 校验场景管理
| 接口 | 方法 | 说明 |
|------|------|------|
| /admin/validateScene/create | POST | 新增校验场景 |
| /admin/validateScene/update | POST | 更新校验场景 |
| /admin/validateScene/delete | POST | 删除校验场景 |
| /admin/validateScene/enable | POST | 启用校验场景 |
| /admin/validateScene/disable | POST | 禁用校验场景 |
| /admin/validateScene/queryPageList | POST | 分页查询校验场景 |
| /admin/validateScene/queryDetail | POST | 根据ID查询校验场景 |

### 校验执行
| 接口 | 方法 | 说明 |
|------|------|------|
| /api/validation/getSceneValidRules | POST | 获取场景校验规则 |


path: references/examples.md
content: # 校验规则管理 - 使用示例

## 环境准备

**方式 1 — 环境变量**（推荐）：
```bash
export VALIDATE_BASE_URL=http://global-masterdata-http.default.yf-bw-test-2.test.51baiwang.com
export VALIDATE_TOKEN=your-token-here
```

**方式 2 — config.json**（项目根目录）：
```json
{
  "baseUrl": "http://global-masterdata-http.default.yf-bw-test-2.test.51baiwang.com",
  "token": "your-token-here",
  "appId": "your-app-id-here"
}
```

---

## 示例 1: 完整新增流程（中国增值税专票）

### 步骤 1: 创建规则组

```bash
node scripts/cli.js create-rule-group \
  --groupName "卖方信息校验组" \
  --description "校验发票卖方相关信息" \
  --orderNum 1
```

输出：`{ "success": true, "data": [{ "id": 1001 }] }`

---

### 步骤 2: 创建规则

```bash
node scripts/cli.js create-rule \
  --groupId 1001 \
  --objectId Invoice \
  --countryCode CN \
  --ruleCode CN-SELLER-001 \
  --ruleName "卖方名称必填校验" \
  --description "校验卖方名称不能为空" \
  --fieldKey sellerName \
  --ruleType required \
  --errorLevel error \
  --errorCode E10001 \
  --errorMessage "{field}不能为空" \
  --executionOrder 1 \
  --status enabled
```

> 💡 值含 `{}`、空格等特殊字符时，shell 引号会自动处理，无需额外转义。

---

### 步骤 3: 创建场景

```bash
node scripts/cli.js create-scene \
  --sceneCode CN_VAT_SPECIAL \
  --sceneName "中国增值税专票场景" \
  --errorStrategy stop_on_error \
  --status enabled
```

---

## 示例 2: 查询操作

```bash
# 查询场景
node scripts/cli.js query-scenes --status enabled --pageNum 1 --pageSize 20

# 查询规则
node scripts/cli.js query-rules --groupId 1001 --countryCode CN --ruleType required

# 查询规则组
node scripts/cli.js query-rule-groups --groupName "卖方信息" --pageSize 10
```

---

## 示例 3: 启用 / 禁用 / 删除

```bash
node scripts/cli.js enable-rule  --id 2001 --status enabled
node scripts/cli.js enable-rule  --id 2001 --status disabled
node scripts/cli.js enable-scene --id 10   --status enabled
node scripts/cli.js delete-rule  --id 2001
node scripts/cli.js delete-scene --id 10
```

---

## 示例 4: 含复杂参数时使用 --json

```bash
node scripts/cli.js create-rule --json '{
  "groupId": 1001,
  "objectId": "Invoice",
  "countryCode": "CN",
  "ruleCode": "CN-SELLER-002",
  "ruleName": "卖方税号格式校验",
  "fieldKey": "sellerTaxNumber",
  "ruleType": "regex",
  "errorMessage": "卖方税号格式不正确",
  "status": "enabled"
}'
```

---

## 直接调用 API（curl / Postman）

### 创建规则组
```json
POST /admin/validateRuleGroup/create
{ "groupName": "卖方信息校验组", "description": "...", "orderNum": 1 }
```

### 查询场景列表
```json
POST /admin/validateScene/queryPageList
{ "sceneCode": "CN_VAT", "status": "enabled", "pageNum": 1, "pageSize": 20 }
```

### 更新规则组顺序
```json
POST /admin/validateRuleGroup/saveGroupOrders
{ "groupOrders": [{ "id": 1001, "orderNum": 1 }, { "id": 1002, "orderNum": 2 }] }
```

### 获取校验对象结构
```json
POST /admin/validateRule/validateEntitySchemas
{ "countryCode": "CN" }
```

---

## 常见规则类型速查

```json
// 必填
{ "ruleType": "required", "fieldKey": "sellerName", "errorMessage": "{field}不能为空" }

// 正则
{ "ruleType": "regex", "fieldKey": "taxNumber", "errorMessage": "税号格式不正确",
  "extensions": { "pattern": "^[0-9A-Z]{15,20}$" } }

// 范围
{ "ruleType": "range", "fieldKey": "amount", "errorMessage": "金额超出范围",
  "extensions": { "min": 0, "max": 999999999 } }

// 长度
{ "ruleType": "length", "fieldKey": "remark", "errorMessage": "备注不能超过200字",
  "extensions": { "max": 200 } }
```


path: lib/api-client.js
content: const https = require('https');
const http = require('http');
const fs = require('fs');
const path = require('path');

/**
 * 加载配置：优先读取环境变量，其次读取 config.json
 * 环境变量：VALIDATE_BASE_URL / VALIDATE_TOKEN / VALIDATE_APP_ID
 */
function loadConfig() {
  // 方式1：环境变量（推荐用于 CI/自动化）
  if (process.env.VALIDATE_BASE_URL && process.env.VALIDATE_TOKEN) {
    return {
      baseUrl: process.env.VALIDATE_BASE_URL.replace(/\/$/, ''),
      [REDACTED_SECRET],
      appId: process.env.VALIDATE_APP_ID || ''
    };
  }

  // 方式2：config.json（从脚本目录或 CWD 向上查找）
  const searchDirs = [
    process.cwd(),
    path.join(process.cwd(), '..'),
    path.dirname(process.argv[1]),
    path.join(path.dirname(process.argv[1]), '..'),
    path.join(path.dirname(process.argv[1]), '../..')
  ];

  for (const dir of searchDirs) {
    const configPath = path.join(dir, 'config.json');
    if (fs.existsSync(configPath)) {
      const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
      const missing = ['baseUrl', 'token'].filter(f => !config[f]);
      if (missing.length > 0) throw new Error(`config.json 缺少字段: ${missing.join(', ')}`);
      config.baseUrl = config.baseUrl.replace(/\/$/, '');
      return config;
    }
  }

  throw new Error(
    '未找到配置。请选择以下任一方式：\n' +
    '  1. 设置环境变量：VALIDATE_BASE_URL=http://... VALIDATE_TOKEN=xxx node scripts/cli.js ...\n' +
    '  2. 在项目根目录创建 config.json：{ "baseUrl": "...", "token": "...", "appId": "..." }'
  );
}

/**
 * 发送 HTTP/HTTPS 请求
 */
function request(url, options = {}) {
  return new Promise((resolve, reject) => {
    const isHttps = url.startsWith('https:');
    const client = isHttps ? https : http;
    const urlObj = new URL(url);

    const reqOptions = {
      hostname: urlObj.hostname,
      port: urlObj.port || (isHttps ? 443 : 80),
      path: urlObj.pathname + urlObj.search,
      method: options.method || 'GET',
      headers: options.headers || {},
      timeout: 30000
    };

    const req = client.request(reqOptions, (res) => {
      let raw = '';
      res.on('data', chunk => raw += chunk);
      res.on('end', () => {
        try {
          resolve({ statusCode: res.statusCode, data: JSON.parse(raw) });
        } catch {
          resolve({ statusCode: res.statusCode, data: raw });
        }
      });
    });

    req.on('timeout', () => {
      req.destroy();
      reject(new Error(`请求超时（30s）：${url}`));
    });

    req.on('error', err => reject(new Error(`网络错误：${err.message}`)));

    if (options.body) req.write(options.body);
    req.end();
  });
}

/**
 * 调用 API
 */
async function apiRequest(endpoint, method = 'POST', body = null) {
  const config = loadConfig();
  const url = `${config.baseUrl}${endpoint}`;

  const options = {
    method,
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Access-Token': config.token
    }
  };

  if (body) {
    const bodyStr = JSON.stringify(body);
    options.body = bodyStr;
    options.headers['Content-Length'] = Buffer.byteLength(bodyStr);
  }

  const response = await request(url, options);

  if (response.statusCode >= 400) {
    const msg = response.data?.message || response.data?.errorMsg || `HTTP ${response.statusCode}`;
    throw new Error(`API 错误 [${response.statusCode}]: ${msg}`);
  }

  // 业务层错误检查（success: false）
  if (response.data && response.data.success === false) {
    const msg = response.data.errorMsg || response.data.message || '业务处理失败';
    throw new Error(`业务错误: ${msg}`);
  }

  return response.data;
}

module.exports = { loadConfig, apiRequest };


path: lib/arg-parser.js
content: /**
 * 命令行参数解析器
 * 支持：--key value | --key=value | --json '{...}'
 */
function parseArgs(argv) {
  const args = argv.slice(2);
  const action = args[0];
  const params = {};

  // --json '整体JSON' 快捷方式
  const jsonIdx = args.indexOf('--json');
  if (jsonIdx !== -1 && args[jsonIdx + 1]) {
    try {
      return { action, params: JSON.parse(args[jsonIdx + 1]) };
    } catch (e) {
      throw new Error(`--json 解析失败：${e.message}`);
    }
  }

  for (let i = 1; i < args.length; i++) {
    const arg = args[i];
    if (!arg.startsWith('--')) continue;

    const eqIdx = arg.indexOf('=');
    if (eqIdx !== -1) {
      // --key=value
      params[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
    } else {
      // --key value
      const key = arg.slice(2);
      const next = args[i + 1];
      if (next !== undefined && !next.startsWith('--')) {
        params[key] = next;
        i++;
      } else {
        params[key] = true;
      }
    }
  }

  return { action, params };
}

module.exports = { parseArgs };


path: lib/utils.js
content: /** 安全转整数 */
function int(v, def = 0) {
  const n = parseInt(v);
  return isNaN(n) ? def : n;
}

/** 断言必填参数存在 */
function requireParams(params, fields) {
  const missing = fields.filter(f => params[f] === undefined || params[f] === '');
  if (missing.length > 0) throw new Error(`缺少必填参数：${missing.join(', ')}`);
}

/** 成功输出 */
function ok(label, data) {
  console.log(`\n✅ ${label}`);
  console.log(JSON.stringify(data, null, 2));
}

module.exports = { int, requireParams, ok };


path: commands/rule-groups.js
content: const { apiRequest } = require('../lib/api-client');
const { int, ok, requireParams } = require('../lib/utils');

/**
 * 规则组操作
 * 接口前缀：/admin/validateRuleGroup
 */
module.exports = {

  async 'create-rule-group'(p) {
    requireParams(p, ['groupName', 'orderNum']);
    const result = await apiRequest('/admin/validateRuleGroup/create', 'POST', {
      groupName:   p.groupName,
      description: p.description || '',
      orderNum:    int(p.orderNum, 1),
      parentId:    int(p.parentId, 0),
      tenantId:    int(p.tenantId, 0),
    });
    ok('规则组创建成功', result);
  },

  async 'query-rule-groups'(p) {
    const result = await apiRequest('/admin/validateRuleGroup/queryPageList', 'POST', {
      id:        p.id        ? int(p.id)       : undefined,
      parentId:  p.parentId  !== undefined ? int(p.parentId) : undefined,
      groupName: p.groupName || '',
      pageNum:   int(p.pageNum,  1),
      pageSize:  int(p.pageSize, 10),
    });
    ok('查询成功', result);
  },

  async 'update-rule-group'(p) {
    requireParams(p, ['id', 'groupName', 'orderNum']);
    const result = await apiRequest('/admin/validateRuleGroup/update', 'POST', {
      id:          int(p.id),
      groupName:   p.groupName,
      description: p.description || '',
      orderNum:    int(p.orderNum, 1),
      parentId:    int(p.parentId, 0),
    });
    ok('规则组更新成功', result);
  },

  async 'delete-rule-group'(p) {
    requireParams(p, ['id']);
    ok('规则组删除成功', await apiRequest('/admin/validateRuleGroup/delete', 'POST', { id: int(p.id) }));
  },

};


path: commands/rules.js
content: const { apiRequest } = require('../lib/api-client');
const { int, ok, requireParams } = require('../lib/utils');

/**
 * 校验规则操作
 * 接口前缀：/admin/validateRule
 */
module.exports = {

  async 'create-rule'(p) {
    requireParams(p, ['groupId', 'objectId', 'countryCode', 'ruleCode', 'ruleName', 'ruleType']);
    const result = await apiRequest('/admin/validateRule/create', 'POST', {
      groupId:        int(p.groupId),
      objectId:       p.objectId,
      countryCode:    p.countryCode,
      ruleCode:       p.ruleCode,
      ruleName:       p.ruleName,
      description:    p.description    || '',
      preconditions:  p.preconditions  || '',
      fieldKey:       p.fieldKey       || '',
      ruleType:       p.ruleType,
      errorLevel:     p.errorLevel     || 'error',
      errorCode:      p.errorCode      || '',
      errorMessage:   p.errorMessage   || '',
      executionOrder: int(p.executionOrder, 1),
      status:         p.status         || 'draft',
      effectiveStart: p.effectiveStart || '',
      effectiveEnd:   p.effectiveEnd   || '',
      extensions:     {},
    });
    ok('规则创建成功', result);
  },

  async 'query-rules'(p) {
    const result = await apiRequest('/admin/validateRule/queryPageList', 'POST', {
      groupId:     p.groupId     ? int(p.groupId) : undefined,
      objectId:    p.objectId    || '',
      countryCode: p.countryCode || '',
      ruleCode:    p.ruleCode    || '',
      ruleName:    p.ruleName    || '',
      fieldKey:    p.fieldKey    || '',
      ruleType:    p.ruleType    || '',
      status:      p.status      || '',
      pageNum:     int(p.pageNum,  1),
      pageSize:    int(p.pageSize, 10),
    });
    ok('查询成功', result);
  },

  async 'enable-rule'(p) {
    requireParams(p, ['id', 'status']);
    const isEnable = p.status === 'enabled';
    const endpoint = isEnable ? '/admin/validateRule/enable' : '/admin/validateRule/disable';
    ok(`规则已${isEnable ? '启用' : '禁用'}`, await apiRequest(endpoint, 'POST', { id: int(p.id) }));
  },

  async 'delete-rule'(p) {
    requireParams(p, ['id']);
    ok('规则删除成功', await apiRequest('/admin/validateRule/delete', 'POST', { id: int(p.id) }));
  },

};


path: commands/scenes.js
content: const { apiRequest } = require('../lib/api-client');
const { int, ok, requireParams } = require('../lib/utils');

/**
 * 校验场景操作
 * 接口前缀：/admin/validateScene
 */
module.exports = {

  async 'create-scene'(p) {
    requireParams(p, ['sceneCode', 'sceneName']);
    const result = await apiRequest('/admin/validateScene/create', 'POST', {
      sceneCode:     p.sceneCode,
      sceneName:     p.sceneName,
      description:   p.description   || '',
      status:        p.status        || 'draft',
      errorStrategy: p.errorStrategy || 'stop_on_error',
      extensions:    {},
      rules:         [],
    });
    ok('场景创建成功', result);
  },

  async 'query-scenes'(p) {
    const result = await apiRequest('/admin/validateScene/queryPageList', 'POST', {
      sceneCode: p.sceneCode || '',
      sceneName: p.sceneName || '',
      status:    p.status    || '',
      pageNum:   int(p.pageNum,  1),
      pageSize:  int(p.pageSize, 10),
    });
    ok('查询成功', result);
  },

  async 'enable-scene'(p) {
    requireParams(p, ['id', 'status']);
    const isEnable = p.status === 'enabled';
    const endpoint = isEnable ? '/admin/validateScene/enable' : '/admin/validateScene/disable';
    ok(`场景已${isEnable ? '启用' : '禁用'}`, await apiRequest(endpoint, 'POST', { id: int(p.id) }));
  },

  async 'delete-scene'(p) {
    requireParams(p, ['id']);
    ok('场景删除成功', await apiRequest('/admin/validateScene/delete', 'POST', { id: int(p.id) }));
  },

};


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)