| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- #!/usr/bin/env node
- /**
- * 批量修补所有 api-config.json 的 errorHandling.balanceInsufficient:
- * 1) 添加 `mess` responseField 检查 (voc-social-api 返回 `mess` 而非 `message`)
- * 2) 在 msg/mess/message 的 contains 列表中追加 "没有开通"、"权限或余额"
- *
- * 目标是让后端 403 `{"code":403,"mess":"没有开通社交平台API权限或余额不足"}`
- * 正确触发 onBalanceInsufficient 流程(而非被错误路由到 onMissing)
- *
- * 运行: node scripts/tools/_patch-errorhandling.js
- * node scripts/tools/_patch-errorhandling.js --dry-run
- */
- const fs = require('fs');
- const path = require('path');
- const ROOT = path.resolve(__dirname, '..', '..');
- const DRY = process.argv.includes('--dry-run');
- const SOURCE_DIRS = [
- 'voc', 'social-media', 'competitor-analysis', 'review-analysis',
- 'synthesis', 'social-voc', 'douyin', 'video-creation',
- 'jimeng', 'payment', 'workshop'
- ];
- // 增量关键词(避免重复添加)
- const EXTRA_KEYWORDS = ['没有开通', '权限或余额'];
- function uniqMerge(existing, extras) {
- const set = new Set(existing);
- extras.forEach(x => set.add(x));
- return Array.from(set);
- }
- function patch(config, filePath) {
- let changed = false;
- const bi = config?.errorHandling?.balanceInsufficient;
- if (!bi || !Array.isArray(bi.conditions)) return false;
- const hasMess = bi.conditions.some(c => c.responseField === 'mess');
- // 收集一个参考的 contains value 列表(优先从 msg 获取;没有则用 message;再没有兜底默认)
- const msgCond = bi.conditions.find(c => c.responseField === 'msg');
- const messageCond = bi.conditions.find(c => c.responseField === 'message');
- const baseValues = (msgCond?.value || messageCond?.value || ['余额不足', 'insufficient', 'balance', 'quota']).slice();
- // 1. 给 msg / message 的 value 追加关键词
- for (const cond of bi.conditions) {
- if (cond.operator !== 'contains') continue;
- if (cond.responseField === 'msg' || cond.responseField === 'message') {
- const merged = uniqMerge(cond.value || [], EXTRA_KEYWORDS);
- if (JSON.stringify(merged) !== JSON.stringify(cond.value)) {
- cond.value = merged;
- changed = true;
- }
- }
- }
- // 2. 追加 mess responseField 条件(若不存在)
- if (!hasMess) {
- const messCond = {
- responseField: 'mess',
- operator: 'contains',
- value: uniqMerge(baseValues, EXTRA_KEYWORDS)
- };
- // 插在 msg 条件之后,保持排序直观
- const insertIdx = bi.conditions.findIndex(c => c.responseField === 'msg');
- if (insertIdx >= 0) {
- bi.conditions.splice(insertIdx + 1, 0, messCond);
- } else {
- bi.conditions.push(messCond);
- }
- changed = true;
- }
- return changed;
- }
- const processed = [];
- for (const dir of SOURCE_DIRS) {
- const catPath = path.join(ROOT, dir);
- if (!fs.existsSync(catPath)) continue;
- for (const skill of fs.readdirSync(catPath)) {
- const p = path.join(catPath, skill, 'api-config.json');
- if (!fs.existsSync(p)) continue;
- try {
- const raw = fs.readFileSync(p, 'utf-8');
- const config = JSON.parse(raw);
- if (patch(config, p)) {
- if (!DRY) {
- fs.writeFileSync(p, JSON.stringify(config, null, 2) + '\n', 'utf-8');
- }
- processed.push({ file: path.relative(ROOT, p), status: DRY ? 'would-change' : 'updated' });
- } else {
- processed.push({ file: path.relative(ROOT, p), status: 'no-change' });
- }
- } catch (e) {
- console.error(`[ERROR] ${p}: ${e.message}`);
- }
- }
- }
- const updated = processed.filter(p => p.status === 'updated' || p.status === 'would-change');
- const skipped = processed.filter(p => p.status === 'no-change');
- console.log(`\n${DRY ? '[DRY]' : '[WRITE]'} 已扫描 ${processed.length} 个 api-config.json`);
- console.log(` 变更: ${updated.length}`);
- console.log(` 无需变更: ${skipped.length}`);
- console.log('');
- updated.forEach(p => console.log(` ✓ ${p.file}`));
- if (skipped.length && updated.length < 20) {
- skipped.forEach(p => console.log(` - ${p.file} (已包含 mess 或无 balanceInsufficient)`));
- }
|