@alipay/alipay-aipay 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/commands/install-current.js +15 -7
- package/dist/commands/install.js +20 -16
- package/dist/commands/uninstall.js +8 -3
- package/dist/commands/update.js +34 -16
- package/dist/index.js +10 -4
- package/dist/utils/channels.js +67 -0
- package/dist/utils/config.js +15 -11
- package/dist/utils/file.js +23 -10
- package/dist/utils/npm.js +19 -33
- package/package.json +8 -3
- package/skill/SKILL.md +2 -0
- package/skill/references/normal/json/release-channel.json +1 -0
- package/skill/references/normal/scripts/self_update.mjs +62 -17
- package/skill/references/normal/self-update.md +3 -1
- package/skill-pre/SKILL.md +65 -0
- package/skill-pre/VERSION +1 -0
- package/skill-pre/references/integration/flow.md +602 -0
- package/skill-pre/references/integration/modules/alipay-sdk-reminder.md +281 -0
- package/skill-pre/references/integration/modules/checklist.md +154 -0
- package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +199 -0
- package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +68 -0
- package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +83 -0
- package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +80 -0
- package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +73 -0
- package/skill-pre/references/integration/modules/code-examples/csharp/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +62 -0
- package/skill-pre/references/integration/modules/code-examples/csharp/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +132 -0
- package/skill-pre/references/integration/modules/code-examples/csharp/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +640 -0
- package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +184 -0
- package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +72 -0
- package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +96 -0
- package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +81 -0
- package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +77 -0
- package/skill-pre/references/integration/modules/code-examples/java/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +69 -0
- package/skill-pre/references/integration/modules/code-examples/java/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +138 -0
- package/skill-pre/references/integration/modules/code-examples/java/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemoController.md +623 -0
- package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +135 -0
- package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +51 -0
- package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +63 -0
- package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +59 -0
- package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +54 -0
- package/skill-pre/references/integration/modules/code-examples/nodejs/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +84 -0
- package/skill-pre/references/integration/modules/code-examples/nodejs/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +71 -0
- package/skill-pre/references/integration/modules/code-examples/nodejs/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +498 -0
- package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +157 -0
- package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +59 -0
- package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +73 -0
- package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +69 -0
- package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +65 -0
- package/skill-pre/references/integration/modules/code-examples/php/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +59 -0
- package/skill-pre/references/integration/modules/code-examples/php/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +110 -0
- package/skill-pre/references/integration/modules/code-examples/php/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +806 -0
- package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +128 -0
- package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +57 -0
- package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +70 -0
- package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +65 -0
- package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +62 -0
- package/skill-pre/references/integration/modules/code-examples/python/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +51 -0
- package/skill-pre/references/integration/modules/code-examples/python/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +47 -0
- package/skill-pre/references/integration/modules/code-examples/python/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +541 -0
- package/skill-pre/references/integration/modules/interface-contract/aipay-interface-contract.md +52 -0
- package/skill-pre/references/integration/modules/interface-contract/apppay-interface-contract.md +45 -0
- package/skill-pre/references/integration/modules/interface-contract/common-trade-contract.md +42 -0
- package/skill-pre/references/integration/modules/interface-contract/notify-url-contract.md +68 -0
- package/skill-pre/references/integration/modules/interface-contract/webpay-interface-contract.md +44 -0
- package/skill-pre/references/integration/modules/interface-guide.md +61 -0
- package/skill-pre/references/integration/modules/product-decision.md +73 -0
- package/skill-pre/references/integration/modules/sandbox/a2m-sandbox-test.md +235 -0
- package/skill-pre/references/integration/modules/sandbox/alipay-sandbox-tool.md +142 -0
- package/skill-pre/references/integration/modules/sandbox/sandbox-setup-guide.md +239 -0
- package/skill-pre/references/integration/modules/scripts/local_402_sandbox_pay.py +900 -0
- package/skill-pre/references/integration/modules/scripts/sandbox_config.sh +347 -0
- package/skill-pre/references/integration/modules/troubleshooting-flow.md +28 -0
- package/skill-pre/references/normal/alipay-cli-env.md +169 -0
- package/skill-pre/references/normal/conversation-telemetry.md +101 -0
- package/skill-pre/references/normal/full-process-routing.md +92 -0
- package/skill-pre/references/normal/integration-worker.md +90 -0
- package/skill-pre/references/normal/json/customer-messages.json +1 -0
- package/skill-pre/references/normal/json/managed-jq-assets.json +1 -0
- package/skill-pre/references/normal/json/policy-rules.json +1 -0
- package/skill-pre/references/normal/json/release-channel.json +1 -0
- package/skill-pre/references/normal/rejection-guide.md +41 -0
- package/skill-pre/references/normal/scripts/agent_platform.mjs +43 -0
- package/skill-pre/references/normal/scripts/alipay_cli_env.mjs +34 -0
- package/skill-pre/references/normal/scripts/alipay_cli_env.sh +55 -0
- package/skill-pre/references/normal/scripts/alipay_cli_refresh.mjs +129 -0
- package/skill-pre/references/normal/scripts/auth_runtime_runner.mjs +60 -0
- package/skill-pre/references/normal/scripts/checklist_sections.mjs +43 -0
- package/skill-pre/references/normal/scripts/common.sh +365 -0
- package/skill-pre/references/normal/scripts/conversation_telemetry.mjs +105 -0
- package/skill-pre/references/normal/scripts/detect_dev_tool.sh +79 -0
- package/skill-pre/references/normal/scripts/flow_loader.mjs +237 -0
- package/skill-pre/references/normal/scripts/integration_context_runner.mjs +151 -0
- package/skill-pre/references/normal/scripts/integration_message_runner.mjs +1456 -0
- package/skill-pre/references/normal/scripts/interaction_reply.mjs +140 -0
- package/skill-pre/references/normal/scripts/managed_jq.mjs +215 -0
- package/skill-pre/references/normal/scripts/onboarding_discovery_runner.mjs +387 -0
- package/skill-pre/references/normal/scripts/onboarding_message_runner.mjs +1425 -0
- package/skill-pre/references/normal/scripts/onboarding_recovery_runner.mjs +100 -0
- package/skill-pre/references/normal/scripts/open_official_url.sh +205 -0
- package/skill-pre/references/normal/scripts/platform_compat.mjs +42 -0
- package/skill-pre/references/normal/scripts/preview_runtime.mjs +151 -0
- package/skill-pre/references/normal/scripts/project_route_inspector.sh +132 -0
- package/skill-pre/references/normal/scripts/python_script_runner.mjs +58 -0
- package/skill-pre/references/normal/scripts/render_customer_message.mjs +1792 -0
- package/skill-pre/references/normal/scripts/runtime.mjs +271 -0
- package/skill-pre/references/normal/scripts/self_update.mjs +376 -0
- package/skill-pre/references/normal/scripts/telemetry.mjs +2102 -0
- package/skill-pre/references/normal/self-update.md +7 -0
- package/skill-pre/references/normal/telemetry.md +70 -0
- package/skill-pre/references/onboarding/assets/keygen.html +927 -0
- package/skill-pre/references/onboarding/flow.md +607 -0
- package/skill-pre/references/onboarding/modules/app-release.md +90 -0
- package/skill-pre/references/onboarding/modules/authorization.md +43 -0
- package/skill-pre/references/onboarding/modules/error-handling.md +62 -0
- package/skill-pre/references/onboarding/modules/product-sign.md +49 -0
- package/skill-pre/references/onboarding/modules/scripts/app.sh +1485 -0
- package/skill-pre/references/onboarding/modules/scripts/ar_sign_apply.sh +331 -0
- package/skill-pre/references/onboarding/modules/scripts/auth.sh +1040 -0
- package/skill-pre/references/onboarding/modules/scripts/download_key_tool.sh +4 -0
- package/skill-pre/references/onboarding/modules/scripts/error_handler.sh +841 -0
- package/skill-pre/references/onboarding/modules/scripts/mcc.sh +1085 -0
- package/skill-pre/references/onboarding/modules/scripts/network_retry.sh +135 -0
- package/skill-pre/references/onboarding/modules/scripts/query_sign_status.sh +105 -0
- package/skill-pre/references/onboarding/modules/scripts/service.sh +528 -0
- package/skill-pre/references/onboarding/modules/scripts/sign_status_parser.sh +58 -0
- package/skill-pre/references/onboarding/modules/scripts/upload_screenshots.sh +208 -0
- package/skill-pre/references/onboarding/modules/scripts/upload_special_license.sh +195 -0
- package/skill-pre/references/onboarding/modules/service-registration.md +40 -0
- package/skill-pre/references/runtime-entry.md +99 -0
- package/dist/commands/install-current.d.ts +0 -4
- package/dist/commands/install-current.d.ts.map +0 -1
- package/dist/commands/install-current.js.map +0 -1
- package/dist/commands/install.d.ts +0 -10
- package/dist/commands/install.d.ts.map +0 -1
- package/dist/commands/install.js.map +0 -1
- package/dist/commands/prepare-tools.d.ts +0 -5
- package/dist/commands/prepare-tools.d.ts.map +0 -1
- package/dist/commands/prepare-tools.js.map +0 -1
- package/dist/commands/uninstall.d.ts +0 -10
- package/dist/commands/uninstall.d.ts.map +0 -1
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/commands/update.d.ts +0 -10
- package/dist/commands/update.d.ts.map +0 -1
- package/dist/commands/update.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/types.d.ts +0 -42
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/alipay-cli.d.ts +0 -20
- package/dist/utils/alipay-cli.d.ts.map +0 -1
- package/dist/utils/alipay-cli.js.map +0 -1
- package/dist/utils/config.d.ts +0 -52
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/file.d.ts +0 -77
- package/dist/utils/file.d.ts.map +0 -1
- package/dist/utils/file.js.map +0 -1
- package/dist/utils/jq.d.ts +0 -17
- package/dist/utils/jq.d.ts.map +0 -1
- package/dist/utils/jq.js.map +0 -1
- package/dist/utils/npm.d.ts +0 -17
- package/dist/utils/npm.d.ts.map +0 -1
- package/dist/utils/npm.js.map +0 -1
- package/dist/utils/shell.d.ts +0 -24
- package/dist/utils/shell.d.ts.map +0 -1
- package/dist/utils/shell.js.map +0 -1
|
@@ -0,0 +1,1085 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#=============================================================================
|
|
3
|
+
# 脚本名称: mcc.sh
|
|
4
|
+
# 功能描述: 查询当前产品支持的 MCC 经营类目并托管 MCC 匹配与方案展示
|
|
5
|
+
# 调用位置: Step 1 方案规划
|
|
6
|
+
# 用法:
|
|
7
|
+
# bash mcc.sh resolve-plan --query <经营描述|MCC编码> --product-type <aipay|webpay|apppay> --root-intent <onboarding_only|full_process> [--semantic-terms-json <JSON数组>] [--agent-candidate-refs-json <JSON数组>] [--select <MCC编码> --selection-receipt <receipt>] [--input-id <USER_INPUT_ID>]
|
|
8
|
+
#=============================================================================
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
|
|
12
|
+
if [ -f "${SCRIPT_DIR}/error_handler.sh" ]; then
|
|
13
|
+
# shellcheck source=/dev/null
|
|
14
|
+
source "${SCRIPT_DIR}/error_handler.sh"
|
|
15
|
+
else
|
|
16
|
+
echo "❌ 缺少错误处理脚本: ${SCRIPT_DIR}/error_handler.sh"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
# shellcheck source=/dev/null
|
|
20
|
+
source "${SCRIPT_DIR}/network_retry.sh"
|
|
21
|
+
|
|
22
|
+
ONBOARDING_RUNNER="${SCRIPT_DIR}/../../../normal/scripts/onboarding_message_runner.mjs"
|
|
23
|
+
MCC_SEMANTIC_RETRY_LIMIT=2
|
|
24
|
+
|
|
25
|
+
require_command jq || exit 1
|
|
26
|
+
require_command node || exit 1
|
|
27
|
+
require_command alipay-cli || exit 1
|
|
28
|
+
require_command mktemp || exit 1
|
|
29
|
+
|
|
30
|
+
product_sales_code() {
|
|
31
|
+
case "$1" in
|
|
32
|
+
aipay) printf 'I1080300001000160457' ;;
|
|
33
|
+
webpay) printf 'I1080300001000041203' ;;
|
|
34
|
+
apppay) printf 'I1080300001000041313' ;;
|
|
35
|
+
*) return 1 ;;
|
|
36
|
+
esac
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
extract_business_json() {
|
|
40
|
+
local raw="$1"
|
|
41
|
+
local business
|
|
42
|
+
business=$(unwrap_mcp "$raw")
|
|
43
|
+
printf '%s' "$business" | jq -ce 'select(type == "object")' 2>/dev/null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
capture_or_exit() {
|
|
47
|
+
local __result_var="$1" output rc
|
|
48
|
+
shift
|
|
49
|
+
output=$("$@")
|
|
50
|
+
rc=$?
|
|
51
|
+
if [ "$rc" -ne 0 ]; then
|
|
52
|
+
[ -z "$output" ] || printf '%s\n' "$output"
|
|
53
|
+
exit "$rc"
|
|
54
|
+
fi
|
|
55
|
+
printf -v "$__result_var" '%s' "$output"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
network_failure() {
|
|
59
|
+
echo "❌ MCC 经营类目查询因网络或服务异常在自动重试两次后仍未恢复"
|
|
60
|
+
emit_control "MCC_FLOW:RETRY_WITH_NETWORK"
|
|
61
|
+
exit 1
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
mcc_query_failed() {
|
|
65
|
+
echo "❌ MCC 经营类目查询结果暂时无法处理,请稍后重试。"
|
|
66
|
+
emit_control "MCC_QUERY_RESULT=FAILED"
|
|
67
|
+
exit 1
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
ensure_mcc_cli_success() {
|
|
71
|
+
local error_type
|
|
72
|
+
error_type=$(detect_error "$1" "MCP_AUTHENTICATED_CALL")
|
|
73
|
+
[ "$error_type" = "SUCCESS" ]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
mcc_cache_ttl_seconds() {
|
|
77
|
+
local value="${ALIPAY_AIPAY_MCC_TREE_CACHE_TTL_SECONDS:-1800}"
|
|
78
|
+
case "$value" in
|
|
79
|
+
''|*[!0-9]*) printf '1800' ;;
|
|
80
|
+
*) printf '%s' "$value" ;;
|
|
81
|
+
esac
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
mcc_cache_scope_id() {
|
|
85
|
+
local candidate="${ALIPAY_AIPAY_RUN_ID:-${ALIPAY_AIPAY_FLOW_RUN_ID:-}}" marker telemetry_script
|
|
86
|
+
if valid_platform_id "$candidate"; then
|
|
87
|
+
printf '%s' "$candidate"
|
|
88
|
+
return 0
|
|
89
|
+
fi
|
|
90
|
+
telemetry_script="${SCRIPT_DIR}/../../../normal/scripts/telemetry.mjs"
|
|
91
|
+
if [ -f "$telemetry_script" ] && command -v node >/dev/null 2>&1; then
|
|
92
|
+
marker="$(node "$telemetry_script" current-run-id 2>&1 >/dev/null || true)"
|
|
93
|
+
candidate="$(printf '%s\n' "$marker" | sed -n 's/^ALIPAY_AIPAY_INTERNAL:RUN_ID=//p' | head -n 1)"
|
|
94
|
+
if valid_platform_id "$candidate"; then
|
|
95
|
+
printf '%s' "$candidate"
|
|
96
|
+
return 0
|
|
97
|
+
fi
|
|
98
|
+
fi
|
|
99
|
+
return 1
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
mcc_cache_root() {
|
|
103
|
+
if [ -n "${ALIPAY_AIPAY_MCC_CACHE_DIR:-}" ]; then
|
|
104
|
+
case "$ALIPAY_AIPAY_MCC_CACHE_DIR" in
|
|
105
|
+
/*) printf '%s' "$ALIPAY_AIPAY_MCC_CACHE_DIR"; return 0 ;;
|
|
106
|
+
*) return 1 ;;
|
|
107
|
+
esac
|
|
108
|
+
fi
|
|
109
|
+
if [ -n "${XDG_CACHE_HOME:-}" ]; then
|
|
110
|
+
case "$XDG_CACHE_HOME" in
|
|
111
|
+
/*) printf '%s/alipay-aipay/mcc-query-tree' "$XDG_CACHE_HOME"; return 0 ;;
|
|
112
|
+
*) return 1 ;;
|
|
113
|
+
esac
|
|
114
|
+
fi
|
|
115
|
+
case "${HOME:-}" in
|
|
116
|
+
/*) [ "$HOME" != "/" ] || return 1; printf '%s/.cache/alipay-aipay/mcc-query-tree' "$HOME" ;;
|
|
117
|
+
*) return 1 ;;
|
|
118
|
+
esac
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
mcc_cache_component() {
|
|
122
|
+
printf '%s' "$1" | LC_ALL=C tr -c 'A-Za-z0-9._-' '_'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
mcc_cache_file() {
|
|
126
|
+
local product_type="$1" sales_code="$2" root scope
|
|
127
|
+
root="$(mcc_cache_root)" || return 1
|
|
128
|
+
scope="$(mcc_cache_scope_id)" || return 1
|
|
129
|
+
if [ -e "$root" ] && { [ ! -d "$root" ] || [ -L "$root" ]; }; then
|
|
130
|
+
return 1
|
|
131
|
+
fi
|
|
132
|
+
mkdir -p "$root" 2>/dev/null || return 1
|
|
133
|
+
chmod 700 "$root" 2>/dev/null || true
|
|
134
|
+
printf '%s/%s.%s.%s.json' \
|
|
135
|
+
"$root" \
|
|
136
|
+
"$(mcc_cache_component "$scope")" \
|
|
137
|
+
"$(mcc_cache_component "$product_type")" \
|
|
138
|
+
"$(mcc_cache_component "$sales_code")"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
mcc_selection_cache_file() {
|
|
142
|
+
local product_type="$1" sales_code="$2" receipt="$3" root scope digest
|
|
143
|
+
root="$(mcc_cache_root)" || return 1
|
|
144
|
+
scope="$(mcc_cache_scope_id)" || return 1
|
|
145
|
+
case "$receipt" in
|
|
146
|
+
AIPAY_MCC_SELECTION_V1.*.*) ;;
|
|
147
|
+
*) return 1 ;;
|
|
148
|
+
esac
|
|
149
|
+
digest="${receipt##*.}"
|
|
150
|
+
case "$digest" in
|
|
151
|
+
''|*[!A-Za-z0-9_-]*) return 1 ;;
|
|
152
|
+
esac
|
|
153
|
+
[ "${#digest}" -ge 32 ] && [ "${#digest}" -le 128 ] || return 1
|
|
154
|
+
if [ -e "$root" ] && { [ ! -d "$root" ] || [ -L "$root" ]; }; then
|
|
155
|
+
return 1
|
|
156
|
+
fi
|
|
157
|
+
mkdir -p "$root" 2>/dev/null || return 1
|
|
158
|
+
chmod 700 "$root" 2>/dev/null || true
|
|
159
|
+
printf '%s/%s.%s.%s.selection.%s.json' \
|
|
160
|
+
"$root" \
|
|
161
|
+
"$(mcc_cache_component "$scope")" \
|
|
162
|
+
"$(mcc_cache_component "$product_type")" \
|
|
163
|
+
"$(mcc_cache_component "$sales_code")" \
|
|
164
|
+
"$digest"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
read_mcc_tree_cache() {
|
|
168
|
+
local product_type="$1" sales_code="$2" file now ttl
|
|
169
|
+
file="$(mcc_cache_file "$product_type" "$sales_code")" || return 1
|
|
170
|
+
[ -f "$file" ] && [ ! -L "$file" ] || return 1
|
|
171
|
+
now="$(date +%s 2>/dev/null)" || return 1
|
|
172
|
+
ttl="$(mcc_cache_ttl_seconds)"
|
|
173
|
+
jq -ce \
|
|
174
|
+
--arg productType "$product_type" \
|
|
175
|
+
--arg salesCode "$sales_code" \
|
|
176
|
+
--argjson now "$now" \
|
|
177
|
+
--argjson ttl "$ttl" '
|
|
178
|
+
if .schemaVersion == 1
|
|
179
|
+
and .productType == $productType
|
|
180
|
+
and .salesCode == $salesCode
|
|
181
|
+
and ((.createdAtEpoch // null) | type == "number")
|
|
182
|
+
and (($now - .createdAtEpoch) >= 0)
|
|
183
|
+
and (($now - .createdAtEpoch) <= $ttl)
|
|
184
|
+
and (.tree.success == true)
|
|
185
|
+
and ((.tree.resultObj.childrenNodes // null) | type == "array")
|
|
186
|
+
then .tree
|
|
187
|
+
else empty
|
|
188
|
+
end
|
|
189
|
+
' "$file" 2>/dev/null
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
write_mcc_tree_cache() {
|
|
193
|
+
local product_type="$1" sales_code="$2" tree="$3" file dir now tmp
|
|
194
|
+
file="$(mcc_cache_file "$product_type" "$sales_code")" || return 0
|
|
195
|
+
dir="${file%/*}"
|
|
196
|
+
now="$(date +%s 2>/dev/null)" || return 0
|
|
197
|
+
tmp="$(mktemp "$dir/.mcc-tree.XXXXXX" 2>/dev/null)" || return 0
|
|
198
|
+
chmod 600 "$tmp" 2>/dev/null || true
|
|
199
|
+
if ! jq -cn \
|
|
200
|
+
--arg productType "$product_type" \
|
|
201
|
+
--arg salesCode "$sales_code" \
|
|
202
|
+
--argjson createdAtEpoch "$now" \
|
|
203
|
+
--argjson tree "$tree" \
|
|
204
|
+
'{
|
|
205
|
+
schemaVersion: 1,
|
|
206
|
+
productType: $productType,
|
|
207
|
+
salesCode: $salesCode,
|
|
208
|
+
createdAtEpoch: $createdAtEpoch,
|
|
209
|
+
tree: $tree
|
|
210
|
+
}' >"$tmp" 2>/dev/null; then
|
|
211
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
212
|
+
return 0
|
|
213
|
+
fi
|
|
214
|
+
mv "$tmp" "$file" 2>/dev/null || {
|
|
215
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
216
|
+
return 0
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
read_mcc_selection_cache() {
|
|
221
|
+
local product_type="$1" sales_code="$2" query="$3" receipt="$4" file now ttl
|
|
222
|
+
file="$(mcc_selection_cache_file "$product_type" "$sales_code" "$receipt")" || return 1
|
|
223
|
+
[ -f "$file" ] && [ ! -L "$file" ] || return 1
|
|
224
|
+
now="$(date +%s 2>/dev/null)" || return 1
|
|
225
|
+
ttl="$(mcc_cache_ttl_seconds)"
|
|
226
|
+
jq -ce \
|
|
227
|
+
--arg productType "$product_type" \
|
|
228
|
+
--arg salesCode "$sales_code" \
|
|
229
|
+
--arg query "$query" \
|
|
230
|
+
--arg receipt "$receipt" \
|
|
231
|
+
--argjson now "$now" \
|
|
232
|
+
--argjson ttl "$ttl" '
|
|
233
|
+
if .schemaVersion == 3
|
|
234
|
+
and .productType == $productType
|
|
235
|
+
and .salesCode == $salesCode
|
|
236
|
+
and .query == $query
|
|
237
|
+
and .selectionReceipt == $receipt
|
|
238
|
+
and ((.semanticTerms // null) | type == "array")
|
|
239
|
+
and ((.semanticTerms | length) <= 12)
|
|
240
|
+
and all(.semanticTerms[]; type == "string")
|
|
241
|
+
and ((.createdAtEpoch // null) | type == "number")
|
|
242
|
+
and (($now - .createdAtEpoch) >= 0)
|
|
243
|
+
and (($now - .createdAtEpoch) <= $ttl)
|
|
244
|
+
and ((.candidateRows // null) | type == "array")
|
|
245
|
+
and ((.candidateRows | length) >= 1)
|
|
246
|
+
and ((.candidateRows | length) <= 8)
|
|
247
|
+
and all(.candidateRows[];
|
|
248
|
+
(.fullCode | type) == "string"
|
|
249
|
+
and (.fullCode | test("^A[0-9]{4}_B[0-9]{4}$"))
|
|
250
|
+
and (.mccCode | type) == "string"
|
|
251
|
+
and (.mccCode | test("^B[0-9]{4}$"))
|
|
252
|
+
and (.treeName | type) == "string"
|
|
253
|
+
and (.treeName | test("\\S"))
|
|
254
|
+
and (.treeDescription | type) == "string"
|
|
255
|
+
and (.treeDescription | test("\\S"))
|
|
256
|
+
and (.treeNeedSpecialQual | type) == "boolean"
|
|
257
|
+
)
|
|
258
|
+
and (
|
|
259
|
+
(all(.candidateRows[]; .treeNeedSpecialQual == false) and .details == null)
|
|
260
|
+
or
|
|
261
|
+
(any(.candidateRows[]; .treeNeedSpecialQual == true)
|
|
262
|
+
and (.details | type) == "object"
|
|
263
|
+
and .details.success == true
|
|
264
|
+
and ((.details.mccDetails // null) | type == "array")
|
|
265
|
+
and all(.details.mccDetails[]; .success == true))
|
|
266
|
+
)
|
|
267
|
+
then {candidateRows: .candidateRows, details: .details}
|
|
268
|
+
else empty
|
|
269
|
+
end
|
|
270
|
+
' "$file" 2>/dev/null
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
write_mcc_selection_cache() {
|
|
274
|
+
local product_type="$1" sales_code="$2" query="$3" receipt="$4" semantic_terms="$5" candidate_rows="$6" details="$7"
|
|
275
|
+
local file dir now tmp
|
|
276
|
+
file="$(mcc_selection_cache_file "$product_type" "$sales_code" "$receipt")" || return 0
|
|
277
|
+
dir="${file%/*}"
|
|
278
|
+
now="$(date +%s 2>/dev/null)" || return 0
|
|
279
|
+
tmp="$(mktemp "$dir/.mcc-selection.XXXXXX" 2>/dev/null)" || return 0
|
|
280
|
+
chmod 600 "$tmp" 2>/dev/null || true
|
|
281
|
+
if ! jq -cn \
|
|
282
|
+
--arg productType "$product_type" \
|
|
283
|
+
--arg salesCode "$sales_code" \
|
|
284
|
+
--arg query "$query" \
|
|
285
|
+
--arg selectionReceipt "$receipt" \
|
|
286
|
+
--argjson semanticTerms "$semantic_terms" \
|
|
287
|
+
--argjson createdAtEpoch "$now" \
|
|
288
|
+
--argjson candidateRows "$candidate_rows" \
|
|
289
|
+
--argjson details "$details" \
|
|
290
|
+
'{
|
|
291
|
+
schemaVersion: 3,
|
|
292
|
+
productType: $productType,
|
|
293
|
+
salesCode: $salesCode,
|
|
294
|
+
query: $query,
|
|
295
|
+
selectionReceipt: $selectionReceipt,
|
|
296
|
+
semanticTerms: $semanticTerms,
|
|
297
|
+
createdAtEpoch: $createdAtEpoch,
|
|
298
|
+
candidateRows: $candidateRows,
|
|
299
|
+
details: $details
|
|
300
|
+
}' >"$tmp" 2>/dev/null; then
|
|
301
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
302
|
+
return 0
|
|
303
|
+
fi
|
|
304
|
+
mv "$tmp" "$file" 2>/dev/null || {
|
|
305
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
306
|
+
return 0
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
mcc_semantic_retry_file() {
|
|
311
|
+
local product_type="$1" sales_code="$2" root scope
|
|
312
|
+
root="$(mcc_cache_root)" || return 1
|
|
313
|
+
scope="$(mcc_cache_scope_id)" || return 1
|
|
314
|
+
if [ -e "$root" ] && { [ ! -d "$root" ] || [ -L "$root" ]; }; then
|
|
315
|
+
return 1
|
|
316
|
+
fi
|
|
317
|
+
mkdir -p "$root" 2>/dev/null || return 1
|
|
318
|
+
chmod 700 "$root" 2>/dev/null || true
|
|
319
|
+
printf '%s/%s.%s.%s.semantic-retries.json' \
|
|
320
|
+
"$root" \
|
|
321
|
+
"$(mcc_cache_component "$scope")" \
|
|
322
|
+
"$(mcc_cache_component "$product_type")" \
|
|
323
|
+
"$(mcc_cache_component "$sales_code")"
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
mcc_agent_review_file() {
|
|
327
|
+
local product_type="$1" sales_code="$2" root scope
|
|
328
|
+
root="$(mcc_cache_root)" || return 1
|
|
329
|
+
scope="$(mcc_cache_scope_id)" || return 1
|
|
330
|
+
if [ -e "$root" ] && { [ ! -d "$root" ] || [ -L "$root" ]; }; then
|
|
331
|
+
return 1
|
|
332
|
+
fi
|
|
333
|
+
mkdir -p "$root" 2>/dev/null || return 1
|
|
334
|
+
chmod 700 "$root" 2>/dev/null || true
|
|
335
|
+
printf '%s/%s.%s.%s.agent-reviews.json' \
|
|
336
|
+
"$root" \
|
|
337
|
+
"$(mcc_cache_component "$scope")" \
|
|
338
|
+
"$(mcc_cache_component "$product_type")" \
|
|
339
|
+
"$(mcc_cache_component "$sales_code")"
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
mcc_agent_review_issued() {
|
|
343
|
+
local product_type="$1" sales_code="$2" input_id="$3" file
|
|
344
|
+
file="$(mcc_agent_review_file "$product_type" "$sales_code")" || return 1
|
|
345
|
+
[ -f "$file" ] && [ ! -L "$file" ] || return 1
|
|
346
|
+
jq -e \
|
|
347
|
+
--arg inputId "$input_id" \
|
|
348
|
+
'.schemaVersion == 1
|
|
349
|
+
and ((.entries // null) | type == "array")
|
|
350
|
+
and all(.entries[];
|
|
351
|
+
type == "object"
|
|
352
|
+
and ((.query // null) | type == "string")
|
|
353
|
+
and ((.inputId // null) | type == "string")
|
|
354
|
+
)
|
|
355
|
+
and any(.entries[]; .inputId == $inputId)' \
|
|
356
|
+
"$file" >/dev/null 2>&1
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
mark_mcc_agent_review_issued() {
|
|
360
|
+
local product_type="$1" sales_code="$2" query="$3" input_id="$4"
|
|
361
|
+
local file dir lock tmp
|
|
362
|
+
file="$(mcc_agent_review_file "$product_type" "$sales_code")" || return 1
|
|
363
|
+
dir="${file%/*}"
|
|
364
|
+
lock="${file}.lock"
|
|
365
|
+
if ! mkdir "$lock" 2>/dev/null; then
|
|
366
|
+
return 1
|
|
367
|
+
fi
|
|
368
|
+
if [ -e "$file" ] && { [ ! -f "$file" ] || [ -L "$file" ]; }; then
|
|
369
|
+
rmdir "$lock" 2>/dev/null || true
|
|
370
|
+
return 1
|
|
371
|
+
fi
|
|
372
|
+
tmp="$(mktemp "$dir/.mcc-agent-reviews.XXXXXX" 2>/dev/null)" || {
|
|
373
|
+
rmdir "$lock" 2>/dev/null || true
|
|
374
|
+
return 1
|
|
375
|
+
}
|
|
376
|
+
chmod 600 "$tmp" 2>/dev/null || true
|
|
377
|
+
if [ -f "$file" ]; then
|
|
378
|
+
if ! jq -ce \
|
|
379
|
+
--arg query "$query" \
|
|
380
|
+
--arg inputId "$input_id" '
|
|
381
|
+
if .schemaVersion != 1 or ((.entries // null) | type != "array") then
|
|
382
|
+
error("invalid MCC agent review state")
|
|
383
|
+
elif any(.entries[]; .query == $query and .inputId == $inputId) then
|
|
384
|
+
.
|
|
385
|
+
else
|
|
386
|
+
.entries += [{query: $query, inputId: $inputId}]
|
|
387
|
+
end
|
|
388
|
+
' "$file" >"$tmp" 2>/dev/null; then
|
|
389
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
390
|
+
rmdir "$lock" 2>/dev/null || true
|
|
391
|
+
return 1
|
|
392
|
+
fi
|
|
393
|
+
else
|
|
394
|
+
if ! jq -cn \
|
|
395
|
+
--arg query "$query" \
|
|
396
|
+
--arg inputId "$input_id" \
|
|
397
|
+
'{schemaVersion: 1, entries: [{query: $query, inputId: $inputId}]}' \
|
|
398
|
+
>"$tmp" 2>/dev/null; then
|
|
399
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
400
|
+
rmdir "$lock" 2>/dev/null || true
|
|
401
|
+
return 1
|
|
402
|
+
fi
|
|
403
|
+
fi
|
|
404
|
+
if ! mv "$tmp" "$file" 2>/dev/null; then
|
|
405
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
406
|
+
rmdir "$lock" 2>/dev/null || true
|
|
407
|
+
return 1
|
|
408
|
+
fi
|
|
409
|
+
rmdir "$lock" 2>/dev/null || true
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
claim_mcc_semantic_retry() {
|
|
413
|
+
local product_type="$1" sales_code="$2" query="$3" input_id="$4" semantic_terms_json="$5"
|
|
414
|
+
local file dir lock entry original_query retry_count next_retry_count now tmp lock_acquired=false
|
|
415
|
+
local current_terms previous_terms missing_previous_terms new_terms
|
|
416
|
+
file="$(mcc_semantic_retry_file "$product_type" "$sales_code")" || return 2
|
|
417
|
+
dir="${file%/*}"
|
|
418
|
+
lock="${file}.lock"
|
|
419
|
+
current_terms="$(printf '%s' "$semantic_terms_json" | jq -ce '
|
|
420
|
+
select(type == "array" and all(.[]; type == "string")) | unique | sort
|
|
421
|
+
' 2>/dev/null)" || return 2
|
|
422
|
+
|
|
423
|
+
# Claim and persist one retry under a bounded private lock. Lock contention
|
|
424
|
+
# is treated as exhausted so a broken local state cannot cause a retry loop.
|
|
425
|
+
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
426
|
+
if mkdir "$lock" 2>/dev/null; then
|
|
427
|
+
lock_acquired=true
|
|
428
|
+
break
|
|
429
|
+
fi
|
|
430
|
+
sleep 0.05
|
|
431
|
+
done
|
|
432
|
+
[ "$lock_acquired" = true ] || return 2
|
|
433
|
+
|
|
434
|
+
if [ -e "$file" ]; then
|
|
435
|
+
if [ ! -f "$file" ] || [ -L "$file" ]; then
|
|
436
|
+
rmdir "$lock" 2>/dev/null || true
|
|
437
|
+
return 2
|
|
438
|
+
fi
|
|
439
|
+
now="$(date +%s 2>/dev/null)" || {
|
|
440
|
+
rmdir "$lock" 2>/dev/null || true
|
|
441
|
+
return 2
|
|
442
|
+
}
|
|
443
|
+
entry="$(jq -cr \
|
|
444
|
+
--arg inputId "$input_id" \
|
|
445
|
+
'
|
|
446
|
+
if .schemaVersion == 1
|
|
447
|
+
and ((.entries // null) | type == "array")
|
|
448
|
+
and all(.entries[];
|
|
449
|
+
type == "object"
|
|
450
|
+
and ((.inputId // null) | type == "string")
|
|
451
|
+
and ((.query // null) | type == "string")
|
|
452
|
+
and ((.count // null) | type == "number")
|
|
453
|
+
and (.count >= 0)
|
|
454
|
+
and ((.count | floor) == .count)
|
|
455
|
+
and ((.updatedAtEpoch // null) | type == "number")
|
|
456
|
+
and ((.semanticTerms // []) | type == "array")
|
|
457
|
+
and all((.semanticTerms // [])[]; type == "string")
|
|
458
|
+
)
|
|
459
|
+
then
|
|
460
|
+
([.entries[] |
|
|
461
|
+
select(
|
|
462
|
+
.inputId == $inputId
|
|
463
|
+
) |
|
|
464
|
+
{query, count, updatedAtEpoch, semanticTerms: (.semanticTerms // [])}
|
|
465
|
+
] | sort_by(.updatedAtEpoch) | last) // null
|
|
466
|
+
else
|
|
467
|
+
error("invalid MCC semantic retry state")
|
|
468
|
+
end
|
|
469
|
+
' "$file" 2>/dev/null)" || {
|
|
470
|
+
rmdir "$lock" 2>/dev/null || true
|
|
471
|
+
return 2
|
|
472
|
+
}
|
|
473
|
+
else
|
|
474
|
+
entry='null'
|
|
475
|
+
fi
|
|
476
|
+
|
|
477
|
+
if [ "$entry" = "null" ]; then
|
|
478
|
+
original_query="$query"
|
|
479
|
+
retry_count=0
|
|
480
|
+
previous_terms='[]'
|
|
481
|
+
else
|
|
482
|
+
original_query="$(printf '%s' "$entry" | jq -r '.query // empty' 2>/dev/null)" || original_query=""
|
|
483
|
+
retry_count="$(printf '%s' "$entry" | jq -r '.count // -1' 2>/dev/null)" || retry_count=-1
|
|
484
|
+
previous_terms="$(printf '%s' "$entry" | jq -c '.semanticTerms // []' 2>/dev/null)" || previous_terms=""
|
|
485
|
+
if [ "$original_query" != "$query" ]; then
|
|
486
|
+
rmdir "$lock" 2>/dev/null || true
|
|
487
|
+
return 2
|
|
488
|
+
fi
|
|
489
|
+
[ -n "$previous_terms" ] || {
|
|
490
|
+
rmdir "$lock" 2>/dev/null || true
|
|
491
|
+
return 2
|
|
492
|
+
}
|
|
493
|
+
missing_previous_terms="$(jq -nr \
|
|
494
|
+
--argjson previous "$previous_terms" \
|
|
495
|
+
--argjson current "$current_terms" \
|
|
496
|
+
'($previous - $current) | length' 2>/dev/null)" || {
|
|
497
|
+
rmdir "$lock" 2>/dev/null || true
|
|
498
|
+
return 2
|
|
499
|
+
}
|
|
500
|
+
new_terms="$(jq -nr \
|
|
501
|
+
--argjson previous "$previous_terms" \
|
|
502
|
+
--argjson current "$current_terms" \
|
|
503
|
+
'($current - $previous) | length' 2>/dev/null)" || {
|
|
504
|
+
rmdir "$lock" 2>/dev/null || true
|
|
505
|
+
return 2
|
|
506
|
+
}
|
|
507
|
+
if [ "$missing_previous_terms" -ne 0 ] || [ "$new_terms" -eq 0 ]; then
|
|
508
|
+
# A semantic retry must be cumulative and add at least one new term.
|
|
509
|
+
# Do not consume a retry budget for an unchanged or replaced term set.
|
|
510
|
+
rmdir "$lock" 2>/dev/null || true
|
|
511
|
+
return 3
|
|
512
|
+
fi
|
|
513
|
+
fi
|
|
514
|
+
case "$retry_count" in
|
|
515
|
+
''|*[!0-9]*) retry_count="$MCC_SEMANTIC_RETRY_LIMIT" ;;
|
|
516
|
+
esac
|
|
517
|
+
if [ "$retry_count" -ge "$MCC_SEMANTIC_RETRY_LIMIT" ]; then
|
|
518
|
+
rmdir "$lock" 2>/dev/null || true
|
|
519
|
+
return 2
|
|
520
|
+
fi
|
|
521
|
+
next_retry_count=$((retry_count + 1))
|
|
522
|
+
now="$(date +%s 2>/dev/null)" || {
|
|
523
|
+
rmdir "$lock" 2>/dev/null || true
|
|
524
|
+
return 2
|
|
525
|
+
}
|
|
526
|
+
tmp="$(mktemp "$dir/.mcc-semantic-retries.XXXXXX" 2>/dev/null)" || {
|
|
527
|
+
rmdir "$lock" 2>/dev/null || true
|
|
528
|
+
return 2
|
|
529
|
+
}
|
|
530
|
+
chmod 600 "$tmp" 2>/dev/null || true
|
|
531
|
+
if [ -f "$file" ] && [ ! -L "$file" ]; then
|
|
532
|
+
if ! jq -ce \
|
|
533
|
+
--arg query "$original_query" \
|
|
534
|
+
--arg inputId "$input_id" \
|
|
535
|
+
--argjson count "$next_retry_count" \
|
|
536
|
+
--argjson now "$now" \
|
|
537
|
+
--argjson semanticTerms "$current_terms" '
|
|
538
|
+
if .schemaVersion != 1 or ((.entries // null) | type != "array") then
|
|
539
|
+
error("invalid MCC semantic retry state")
|
|
540
|
+
else
|
|
541
|
+
.entries = (
|
|
542
|
+
[.entries[] | select(.inputId != $inputId)]
|
|
543
|
+
+ [{
|
|
544
|
+
query: $query,
|
|
545
|
+
inputId: $inputId,
|
|
546
|
+
count: $count,
|
|
547
|
+
updatedAtEpoch: $now,
|
|
548
|
+
semanticTerms: $semanticTerms
|
|
549
|
+
}]
|
|
550
|
+
)
|
|
551
|
+
| .entries |= sort_by(.updatedAtEpoch)
|
|
552
|
+
end
|
|
553
|
+
' "$file" >"$tmp" 2>/dev/null; then
|
|
554
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
555
|
+
rmdir "$lock" 2>/dev/null || true
|
|
556
|
+
return 2
|
|
557
|
+
fi
|
|
558
|
+
elif [ -e "$file" ]; then
|
|
559
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
560
|
+
rmdir "$lock" 2>/dev/null || true
|
|
561
|
+
return 2
|
|
562
|
+
else
|
|
563
|
+
if ! jq -cn \
|
|
564
|
+
--arg query "$original_query" \
|
|
565
|
+
--arg inputId "$input_id" \
|
|
566
|
+
--argjson count "$next_retry_count" \
|
|
567
|
+
--argjson now "$now" \
|
|
568
|
+
--argjson semanticTerms "$current_terms" \
|
|
569
|
+
'{
|
|
570
|
+
schemaVersion: 1,
|
|
571
|
+
entries: [{
|
|
572
|
+
query: $query,
|
|
573
|
+
inputId: $inputId,
|
|
574
|
+
count: $count,
|
|
575
|
+
updatedAtEpoch: $now,
|
|
576
|
+
semanticTerms: $semanticTerms
|
|
577
|
+
}]
|
|
578
|
+
}' >"$tmp" 2>/dev/null; then
|
|
579
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
580
|
+
rmdir "$lock" 2>/dev/null || true
|
|
581
|
+
return 2
|
|
582
|
+
fi
|
|
583
|
+
fi
|
|
584
|
+
if ! mv "$tmp" "$file" 2>/dev/null; then
|
|
585
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
586
|
+
rmdir "$lock" 2>/dev/null || true
|
|
587
|
+
return 2
|
|
588
|
+
fi
|
|
589
|
+
rmdir "$lock" 2>/dev/null || true
|
|
590
|
+
printf '%s' "$next_retry_count"
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
get_mcc_tree() {
|
|
594
|
+
local product_type="$1" sales_code="$2" cached tree cached_rows
|
|
595
|
+
cached="$(read_mcc_tree_cache "$product_type" "$sales_code" 2>/dev/null || true)"
|
|
596
|
+
if [ -n "$cached" ]; then
|
|
597
|
+
cached_rows="$(printf '%s' "$cached" | mcc_rows_from_tree 2>/dev/null || true)"
|
|
598
|
+
if printf '%s' "$cached_rows" | jq -e 'type == "array" and length > 0' >/dev/null 2>&1; then
|
|
599
|
+
printf '%s' "$cached"
|
|
600
|
+
return 0
|
|
601
|
+
fi
|
|
602
|
+
fi
|
|
603
|
+
capture_or_exit tree query_mcc_tree "$sales_code"
|
|
604
|
+
write_mcc_tree_cache "$product_type" "$sales_code" "$tree"
|
|
605
|
+
printf '%s' "$tree"
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
query_mcc_tree() {
|
|
609
|
+
local sales_code="$1" request_json raw retry_rc business rows
|
|
610
|
+
request_json=$(jq -n \
|
|
611
|
+
--arg salesCode "$sales_code" \
|
|
612
|
+
'{request:{channelCode:"B_SK_SH_RPC",salesProductCodes:[$salesCode]},ctx:{}}') || return 1
|
|
613
|
+
|
|
614
|
+
export PLATFORM="${DEV_TOOL_NAME}" PLATFORM_ID="${PLATFORM_ID:-}"
|
|
615
|
+
run_network_retry raw read query_mcc -- alipay-cli mcp call ar-support.queryMcc \
|
|
616
|
+
-d "$request_json" --json
|
|
617
|
+
retry_rc=$?
|
|
618
|
+
if [ "$retry_rc" -eq "${ALIPAY_CLI_LOCAL_STATE_PERMISSION_EXIT_CODE:-77}" ]; then
|
|
619
|
+
exit "$retry_rc"
|
|
620
|
+
fi
|
|
621
|
+
[ "$retry_rc" -eq 0 ] || network_failure
|
|
622
|
+
ensure_mcc_cli_success "$raw" || mcc_query_failed
|
|
623
|
+
business=$(extract_business_json "$raw") || {
|
|
624
|
+
mcc_query_failed
|
|
625
|
+
}
|
|
626
|
+
if ! printf '%s' "$business" | jq -e '.success == true and (.resultObj.childrenNodes | type == "array")' >/dev/null 2>&1; then
|
|
627
|
+
mcc_query_failed
|
|
628
|
+
fi
|
|
629
|
+
rows="$(printf '%s' "$business" | mcc_rows_from_tree 2>/dev/null || true)"
|
|
630
|
+
if ! printf '%s' "$rows" | jq -e 'type == "array" and length > 0' >/dev/null 2>&1; then
|
|
631
|
+
mcc_query_failed
|
|
632
|
+
fi
|
|
633
|
+
printf '%s' "$business"
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
mcc_rows_from_tree() {
|
|
637
|
+
jq -ce '
|
|
638
|
+
def nonblank_string: type == "string" and test("\\S");
|
|
639
|
+
def mcc_row($primary):
|
|
640
|
+
. as $node |
|
|
641
|
+
if (($node.code? | type) != "string" or ($node.code | test("^B[0-9]{4}$") | not)) then
|
|
642
|
+
error("queryMcc returned invalid selectable mcc code")
|
|
643
|
+
elif (($node.name? | nonblank_string) | not) then
|
|
644
|
+
error("queryMcc returned missing mcc name")
|
|
645
|
+
elif (($node.description? | nonblank_string) | not) then
|
|
646
|
+
error("queryMcc returned missing mcc description")
|
|
647
|
+
elif (($node.needSpecialQual? | type) != "boolean") then
|
|
648
|
+
error("queryMcc returned invalid needSpecialQual")
|
|
649
|
+
elif (($node.childrenNodes? // []) | type) != "array"
|
|
650
|
+
or (($node.childrenNodes? // []) | length) > 0 then
|
|
651
|
+
error("queryMcc returned invalid selectable mcc hierarchy")
|
|
652
|
+
else
|
|
653
|
+
{
|
|
654
|
+
fullCode: ($primary + "_" + $node.code),
|
|
655
|
+
mccCode: $node.code,
|
|
656
|
+
treeName: $node.name,
|
|
657
|
+
treeDescription: $node.description,
|
|
658
|
+
treeNeedSpecialQual: $node.needSpecialQual
|
|
659
|
+
}
|
|
660
|
+
end;
|
|
661
|
+
[
|
|
662
|
+
.resultObj.childrenNodes[]? |
|
|
663
|
+
. as $primary |
|
|
664
|
+
if (($primary.code? | type) != "string" or ($primary.code | test("^A[0-9]{4}$") | not)) then
|
|
665
|
+
error("queryMcc returned invalid primary mcc code")
|
|
666
|
+
elif (($primary.childrenNodes? | type) != "array") then
|
|
667
|
+
error("queryMcc returned invalid primary mcc hierarchy")
|
|
668
|
+
else
|
|
669
|
+
$primary.childrenNodes[]? | mcc_row($primary.code)
|
|
670
|
+
end
|
|
671
|
+
] as $rows |
|
|
672
|
+
if ($rows | length) == 0 then
|
|
673
|
+
error("queryMcc returned no selectable mcc")
|
|
674
|
+
elif (($rows | map(.fullCode) | unique | length) != ($rows | length)) then
|
|
675
|
+
error("queryMcc returned duplicate selectable mcc")
|
|
676
|
+
else
|
|
677
|
+
($rows
|
|
678
|
+
| sort_by(.fullCode)
|
|
679
|
+
| to_entries
|
|
680
|
+
| map(.value + {mccRef: ("mcc-" + ((.key + 1) | tostring))}))
|
|
681
|
+
end
|
|
682
|
+
' 2>/dev/null
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
normalize_mcc_tree_catalog() {
|
|
686
|
+
local rows_json="$1"
|
|
687
|
+
jq -cn --argjson rows "$rows_json" '
|
|
688
|
+
($rows |
|
|
689
|
+
map({
|
|
690
|
+
mccCode: .fullCode,
|
|
691
|
+
mccName: .treeName,
|
|
692
|
+
mccDescription: .treeDescription,
|
|
693
|
+
needSpecialQual: .treeNeedSpecialQual,
|
|
694
|
+
qualificationGroups: []
|
|
695
|
+
}) |
|
|
696
|
+
sort_by(.mccCode) |
|
|
697
|
+
to_entries |
|
|
698
|
+
map(.value + {mccRef: ("mcc-" + ((.key + 1) | tostring))}))
|
|
699
|
+
' 2>/dev/null
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
rows_for_full_codes() {
|
|
703
|
+
local rows_json="$1" full_codes_json="$2"
|
|
704
|
+
jq -cn --argjson rows "$rows_json" --argjson fullCodes "$full_codes_json" '
|
|
705
|
+
($fullCodes | unique) as $wanted |
|
|
706
|
+
[
|
|
707
|
+
$rows[] |
|
|
708
|
+
select(.fullCode as $code | $wanted | index($code))
|
|
709
|
+
]
|
|
710
|
+
' 2>/dev/null
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
query_mcc_details() {
|
|
714
|
+
local rows_json="$1" mcc_codes_json total details
|
|
715
|
+
mcc_codes_json=$(printf '%s' "$rows_json" | jq -c '[.[].mccCode] | unique' 2>/dev/null) || return 1
|
|
716
|
+
if ! printf '%s' "$mcc_codes_json" | jq -e 'length > 0' >/dev/null 2>&1; then
|
|
717
|
+
mcc_query_failed
|
|
718
|
+
fi
|
|
719
|
+
total=$(printf '%s' "$mcc_codes_json" | jq -r 'length' 2>/dev/null) || mcc_query_failed
|
|
720
|
+
[ "$total" -le 8 ] || mcc_query_failed
|
|
721
|
+
capture_or_exit details query_mcc_detail_chunk "$mcc_codes_json"
|
|
722
|
+
printf '%s' "$details"
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
query_mcc_detail_chunk() {
|
|
726
|
+
local mcc_codes_json="$1" request_json raw retry_rc business
|
|
727
|
+
request_json=$(jq -n --argjson mccCodes "$mcc_codes_json" '{request:{mccCodes:$mccCodes}}') || return 1
|
|
728
|
+
|
|
729
|
+
export PLATFORM="${DEV_TOOL_NAME}" PLATFORM_ID="${PLATFORM_ID:-}"
|
|
730
|
+
run_network_retry raw read batch_query_mcc -- alipay-cli mcp call mccsearchservice.batchQueryMcc \
|
|
731
|
+
-d "$request_json" --json
|
|
732
|
+
retry_rc=$?
|
|
733
|
+
if [ "$retry_rc" -eq "${ALIPAY_CLI_LOCAL_STATE_PERMISSION_EXIT_CODE:-77}" ]; then
|
|
734
|
+
exit "$retry_rc"
|
|
735
|
+
fi
|
|
736
|
+
[ "$retry_rc" -eq 0 ] || network_failure
|
|
737
|
+
ensure_mcc_cli_success "$raw" || mcc_query_failed
|
|
738
|
+
business=$(extract_business_json "$raw") || {
|
|
739
|
+
mcc_query_failed
|
|
740
|
+
}
|
|
741
|
+
if ! printf '%s' "$business" | jq -e '.success == true and (.mccDetails | type == "array") and (.truncated // false | not)' >/dev/null 2>&1; then
|
|
742
|
+
mcc_query_failed
|
|
743
|
+
fi
|
|
744
|
+
printf '%s' "$business"
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
mcc_match_error_kind() {
|
|
748
|
+
case "$1" in
|
|
749
|
+
*agent-candidate-refs-json*) printf 'INVALID_AGENT_CANDIDATE_REFS' ;;
|
|
750
|
+
*semantic-terms-json*) printf 'INVALID_SEMANTIC_TERMS' ;;
|
|
751
|
+
*mcc-catalog-json*) printf 'INVALID_DYNAMIC_CATALOG' ;;
|
|
752
|
+
*) printf 'MATCHER_RUNTIME_ERROR' ;;
|
|
753
|
+
esac
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
normalize_mcc_catalog() {
|
|
757
|
+
local rows_json="$1" details_json="$2"
|
|
758
|
+
jq -cn --argjson rows "$rows_json" --argjson details "$details_json" '
|
|
759
|
+
($details.mccDetails // []) as $detailList |
|
|
760
|
+
($rows | map(select(.treeNeedSpecialQual == true) | .mccCode) | unique) as $requiredCodes |
|
|
761
|
+
($detailList | map(select(.success != true) | .mccCode) | unique) as $failedCodes |
|
|
762
|
+
if ($failedCodes | length) > 0 then
|
|
763
|
+
error("batchQueryMcc returned failed mcc details")
|
|
764
|
+
else
|
|
765
|
+
($detailList | map(.mccCode) | unique) as $detailCodes |
|
|
766
|
+
($requiredCodes - $detailCodes) as $missingCodes |
|
|
767
|
+
($detailCodes - $requiredCodes) as $unexpectedCodes |
|
|
768
|
+
if (($detailCodes | length) != ($detailList | length)) then
|
|
769
|
+
error("batchQueryMcc returned duplicate mcc details")
|
|
770
|
+
elif ($missingCodes | length) > 0 then
|
|
771
|
+
error("batchQueryMcc missed required qualification details")
|
|
772
|
+
elif ($unexpectedCodes | length) > 0 then
|
|
773
|
+
error("batchQueryMcc returned unrequested mcc details")
|
|
774
|
+
else
|
|
775
|
+
(reduce $detailList[] as $detail ({}; .[$detail.mccCode] = $detail)) as $detailByCode |
|
|
776
|
+
[
|
|
777
|
+
$rows[] |
|
|
778
|
+
. as $row |
|
|
779
|
+
($detailByCode[$row.mccCode]) as $detail |
|
|
780
|
+
{
|
|
781
|
+
mccCode: $row.fullCode,
|
|
782
|
+
mccName: $row.treeName,
|
|
783
|
+
mccDescription: $row.treeDescription,
|
|
784
|
+
needSpecialQual: $row.treeNeedSpecialQual,
|
|
785
|
+
mccRef: $row.mccRef,
|
|
786
|
+
qualificationGroups: (if $row.treeNeedSpecialQual then
|
|
787
|
+
(($detail.qualificationGroups // []) | map({
|
|
788
|
+
qualifications: ((.qualifications // []) | map({
|
|
789
|
+
attaType: (if (.attaType == null or .attaType == "") then null else (.attaType | tostring) end),
|
|
790
|
+
qualId: (if (.qualId == null) then null else (.qualId | tostring) end),
|
|
791
|
+
qualName: ((.qualName // "") | tostring),
|
|
792
|
+
qualType: (if (.qualType == null) then null else (.qualType | tostring) end),
|
|
793
|
+
status: (if (.status == null) then null else (.status | tostring) end)
|
|
794
|
+
}))
|
|
795
|
+
}))
|
|
796
|
+
else
|
|
797
|
+
[]
|
|
798
|
+
end)
|
|
799
|
+
}
|
|
800
|
+
] as $catalog |
|
|
801
|
+
if any($catalog[];
|
|
802
|
+
(.needSpecialQual == true) !=
|
|
803
|
+
(([.qualificationGroups[]? | select((.qualifications | length) > 0)] | length) > 0)
|
|
804
|
+
) or any($catalog[]; any(.qualificationGroups[]?; (.qualifications | length) == 0)) then
|
|
805
|
+
error("batchQueryMcc returned inconsistent qualification groups")
|
|
806
|
+
else
|
|
807
|
+
$catalog
|
|
808
|
+
end
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
' 2>/dev/null
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
run_mcc_match_codes() {
|
|
815
|
+
local query="$1" product_type="$2" root_intent="$3" catalog="$4" semantic_terms_json="$5" agent_candidate_refs_json="${6:-}" runner_out runner_err runner_rc line resolution=""
|
|
816
|
+
local runner_error error_kind
|
|
817
|
+
runner_out=$(mktemp "${TMPDIR:-/tmp}/mcc_match_out.XXXXXX") || { echo "❌ 创建临时文件失败"; exit 1; }
|
|
818
|
+
runner_err=$(mktemp "${TMPDIR:-/tmp}/mcc_match_err.XXXXXX") || { rm -f "$runner_out"; echo "❌ 创建临时文件失败"; exit 1; }
|
|
819
|
+
local node_args=(
|
|
820
|
+
"$ONBOARDING_RUNNER" mcc-match-codes
|
|
821
|
+
--query "$query" \
|
|
822
|
+
--product-type "$product_type" \
|
|
823
|
+
--root-intent "$root_intent" \
|
|
824
|
+
--mcc-catalog-json "$catalog" \
|
|
825
|
+
--semantic-terms-json "$semantic_terms_json"
|
|
826
|
+
)
|
|
827
|
+
if [ -n "$agent_candidate_refs_json" ]; then
|
|
828
|
+
node_args+=(--agent-candidate-refs-json "$agent_candidate_refs_json")
|
|
829
|
+
fi
|
|
830
|
+
"${NODE_BINARY:-node}" "${node_args[@]}" >"$runner_out" 2>"$runner_err"
|
|
831
|
+
runner_rc=$?
|
|
832
|
+
if [ "$runner_rc" -ne 0 ] || [ -s "$runner_out" ]; then
|
|
833
|
+
runner_error="$(sed -n 's/^ONBOARDING_MESSAGE_ERROR://p' "$runner_err" | sed -n '1p')"
|
|
834
|
+
error_kind="$(mcc_match_error_kind "$runner_error")"
|
|
835
|
+
if [ "$error_kind" = "INVALID_AGENT_CANDIDATE_REFS" ] && [ -n "$agent_candidate_refs_json" ]; then
|
|
836
|
+
rm -f "$runner_out" "$runner_err"
|
|
837
|
+
emit_control "MCC_AGENT_CANDIDATE_REFS_IGNORED=INVALID"
|
|
838
|
+
run_mcc_match_codes "$query" "$product_type" "$root_intent" "$catalog" "$semantic_terms_json"
|
|
839
|
+
return $?
|
|
840
|
+
fi
|
|
841
|
+
rm -f "$runner_out" "$runner_err"
|
|
842
|
+
emit_control "MCC_MATCH_ERROR=$error_kind"
|
|
843
|
+
mcc_query_failed
|
|
844
|
+
fi
|
|
845
|
+
while IFS= read -r line; do
|
|
846
|
+
case "$line" in
|
|
847
|
+
ALIPAY_AIPAY_INTERNAL:MCC_RESOLUTION=*)
|
|
848
|
+
resolution="${line#ALIPAY_AIPAY_INTERNAL:MCC_RESOLUTION=}" ;;
|
|
849
|
+
esac
|
|
850
|
+
done <"$runner_err"
|
|
851
|
+
rm -f "$runner_out" "$runner_err"
|
|
852
|
+
[ -n "$resolution" ] || mcc_query_failed
|
|
853
|
+
printf '%s' "$resolution"
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
validate_mcc_selection_cache() {
|
|
857
|
+
local query="$1" product_type="$2" selection_receipt="$3" catalog="$4" semantic_terms_json="$5"
|
|
858
|
+
"${NODE_BINARY:-node}" "$ONBOARDING_RUNNER" mcc-validate-selection-cache \
|
|
859
|
+
--query "$query" \
|
|
860
|
+
--product-type "$product_type" \
|
|
861
|
+
--selection-receipt "$selection_receipt" \
|
|
862
|
+
--mcc-catalog-json "$catalog" \
|
|
863
|
+
--semantic-terms-json "$semantic_terms_json" >/dev/null 2>&1
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
run_resolve_plan() {
|
|
867
|
+
local query="" product_type="" root_intent="" semantic_terms_json='[]' agent_candidate_refs_json="" select_value="" selection_receipt="" input_id="" sales_code tree rows tree_catalog resolution match_type semantic_status full_codes candidate_rows qualification_rows details catalog selection_cache="" used_selection_cache=false stale_selection=false resolved_semantic_terms='[]' retry_rc=0 next_retry_count=0 retry_reason="" retry_payload="" agent_review_required="false" agent_review_payload=""
|
|
868
|
+
shift
|
|
869
|
+
while [[ $# -gt 0 ]]; do
|
|
870
|
+
case "$1" in
|
|
871
|
+
--query)
|
|
872
|
+
[ $# -ge 2 ] || { echo "❌ 缺少参数值: $1"; exit 1; }
|
|
873
|
+
query="$2"; shift 2 ;;
|
|
874
|
+
--product-type)
|
|
875
|
+
[ $# -ge 2 ] || { echo "❌ 缺少参数值: $1"; exit 1; }
|
|
876
|
+
product_type="$2"; shift 2 ;;
|
|
877
|
+
--root-intent)
|
|
878
|
+
[ $# -ge 2 ] || { echo "❌ 缺少参数值: $1"; exit 1; }
|
|
879
|
+
root_intent="$2"; shift 2 ;;
|
|
880
|
+
--semantic-terms-json)
|
|
881
|
+
[ $# -ge 2 ] || { echo "❌ 缺少参数值: $1"; exit 1; }
|
|
882
|
+
semantic_terms_json="$2"; shift 2 ;;
|
|
883
|
+
--agent-candidate-refs-json)
|
|
884
|
+
[ $# -ge 2 ] || { echo "❌ 缺少参数值: $1"; exit 1; }
|
|
885
|
+
agent_candidate_refs_json="$2"; shift 2 ;;
|
|
886
|
+
--select)
|
|
887
|
+
[ $# -ge 2 ] || { echo "❌ 缺少参数值: $1"; exit 1; }
|
|
888
|
+
select_value="$2"; shift 2 ;;
|
|
889
|
+
--selection-receipt)
|
|
890
|
+
[ $# -ge 2 ] || { echo "❌ 缺少参数值: $1"; exit 1; }
|
|
891
|
+
selection_receipt="$2"; shift 2 ;;
|
|
892
|
+
--input-id)
|
|
893
|
+
[ $# -ge 2 ] || { echo "❌ 缺少参数值: $1"; exit 1; }
|
|
894
|
+
input_id="$2"; shift 2 ;;
|
|
895
|
+
*)
|
|
896
|
+
echo "❌ 未知参数: $1"; exit 1 ;;
|
|
897
|
+
esac
|
|
898
|
+
done
|
|
899
|
+
if [ -z "$query" ] || [ -z "$product_type" ]; then
|
|
900
|
+
echo "❌ 缺少 --query 或 --product-type"
|
|
901
|
+
exit 1
|
|
902
|
+
fi
|
|
903
|
+
case "$root_intent" in
|
|
904
|
+
onboarding_only|full_process) ;;
|
|
905
|
+
*)
|
|
906
|
+
echo "❌ 缺少或非法 --root-intent,仅支持 onboarding_only、full_process"
|
|
907
|
+
exit 1
|
|
908
|
+
;;
|
|
909
|
+
esac
|
|
910
|
+
semantic_terms_json=$(printf '%s' "$semantic_terms_json" | jq -ce '
|
|
911
|
+
select(type == "array" and length <= 12 and all(.[]; type == "string"))
|
|
912
|
+
' 2>/dev/null) || {
|
|
913
|
+
echo "❌ --semantic-terms-json 必须是最多 12 项的 JSON 字符串数组"
|
|
914
|
+
exit 1
|
|
915
|
+
}
|
|
916
|
+
sales_code=$(product_sales_code "$product_type") || {
|
|
917
|
+
echo "❌ product-type 非法,仅支持 aipay|webpay|apppay"
|
|
918
|
+
exit 1
|
|
919
|
+
}
|
|
920
|
+
init_alipay_cli_product "$product_type" || exit 1
|
|
921
|
+
|
|
922
|
+
if [ -n "$selection_receipt" ]; then
|
|
923
|
+
selection_cache="$(read_mcc_selection_cache "$product_type" "$sales_code" "$query" "$selection_receipt" 2>/dev/null || true)"
|
|
924
|
+
if [ -n "$selection_cache" ]; then
|
|
925
|
+
candidate_rows=$(printf '%s' "$selection_cache" | jq -c '.candidateRows' 2>/dev/null) || mcc_query_failed
|
|
926
|
+
details=$(printf '%s' "$selection_cache" | jq -c '.details' 2>/dev/null) || mcc_query_failed
|
|
927
|
+
catalog=$(normalize_mcc_catalog "$candidate_rows" "$details" 2>/dev/null || true)
|
|
928
|
+
if [ -n "$catalog" ] && validate_mcc_selection_cache "$query" "$product_type" "$selection_receipt" "$catalog" "$semantic_terms_json"; then
|
|
929
|
+
used_selection_cache=true
|
|
930
|
+
else
|
|
931
|
+
catalog=""
|
|
932
|
+
candidate_rows=""
|
|
933
|
+
details=""
|
|
934
|
+
fi
|
|
935
|
+
fi
|
|
936
|
+
[ "$used_selection_cache" = true ] || stale_selection=true
|
|
937
|
+
fi
|
|
938
|
+
|
|
939
|
+
if [ "$used_selection_cache" != true ]; then
|
|
940
|
+
require_alipay_cli_local_state_access_or_emit || exit "$?"
|
|
941
|
+
capture_or_exit tree get_mcc_tree "$product_type" "$sales_code"
|
|
942
|
+
rows=$(printf '%s' "$tree" | mcc_rows_from_tree) || {
|
|
943
|
+
mcc_query_failed
|
|
944
|
+
}
|
|
945
|
+
tree_catalog=$(normalize_mcc_tree_catalog "$rows") || {
|
|
946
|
+
mcc_query_failed
|
|
947
|
+
}
|
|
948
|
+
capture_or_exit resolution run_mcc_match_codes "$query" "$product_type" "$root_intent" "$tree_catalog" "$semantic_terms_json" "$agent_candidate_refs_json"
|
|
949
|
+
match_type=$(printf '%s' "$resolution" | jq -r '.matchType // ""' 2>/dev/null) || mcc_query_failed
|
|
950
|
+
semantic_status=$(printf '%s' "$resolution" | jq -r '.semanticStatus // ""' 2>/dev/null) || mcc_query_failed
|
|
951
|
+
resolved_semantic_terms=$(printf '%s' "$resolution" | jq -c '.semanticTerms // []' 2>/dev/null) || mcc_query_failed
|
|
952
|
+
agent_review_required=$(printf '%s' "$resolution" | jq -r '.agentReview.required // false' 2>/dev/null) || mcc_query_failed
|
|
953
|
+
if [ -n "$input_id" ] && [ -n "$agent_candidate_refs_json" ]; then
|
|
954
|
+
mark_mcc_agent_review_issued "$product_type" "$sales_code" "$query" "$input_id" || true
|
|
955
|
+
fi
|
|
956
|
+
if [ "$agent_review_required" = "true" ] && [ -z "$agent_candidate_refs_json" ] && [ -n "$input_id" ]; then
|
|
957
|
+
agent_review_payload=$(printf '%s' "$resolution" | jq -c '.agentReview' 2>/dev/null) || mcc_query_failed
|
|
958
|
+
if ! mcc_agent_review_issued "$product_type" "$sales_code" "$input_id"; then
|
|
959
|
+
mark_mcc_agent_review_issued "$product_type" "$sales_code" "$query" "$input_id" || true
|
|
960
|
+
emit_control "MCC_QUERY_RESULT=SUCCESS"
|
|
961
|
+
emit_control "MCC_RESOLUTION=$(printf '%s' "$resolution" | jq -c 'del(.agentReview)' 2>/dev/null)"
|
|
962
|
+
emit_control "MCC_AGENT_REVIEW_REQUIRED=$agent_review_payload"
|
|
963
|
+
return 0
|
|
964
|
+
fi
|
|
965
|
+
fi
|
|
966
|
+
if [ "$match_type" = "NONE" ] \
|
|
967
|
+
&& [ "$semantic_status" != "DYNAMIC_CATALOG_CODE_NOT_FOUND" ] \
|
|
968
|
+
&& [ "$semantic_status" != "DYNAMIC_CATALOG_CODE_AMBIGUOUS" ]; then
|
|
969
|
+
if [ -n "$input_id" ]; then
|
|
970
|
+
next_retry_count="$(claim_mcc_semantic_retry \
|
|
971
|
+
"$product_type" "$sales_code" "$query" "$input_id" "$resolved_semantic_terms" 2>/dev/null)"
|
|
972
|
+
retry_rc=$?
|
|
973
|
+
else
|
|
974
|
+
# A retry without a message identity cannot be bound to one user input.
|
|
975
|
+
# Keep the bounded retry contract closed instead of sharing a placeholder.
|
|
976
|
+
retry_rc=2
|
|
977
|
+
fi
|
|
978
|
+
if [ "$retry_rc" -eq 0 ]; then
|
|
979
|
+
if [ "$semantic_terms_json" = "[]" ]; then
|
|
980
|
+
retry_reason="MISSING_SEMANTIC_TERMS"
|
|
981
|
+
else
|
|
982
|
+
retry_reason="NO_MATCH"
|
|
983
|
+
fi
|
|
984
|
+
retry_payload=$(jq -cn \
|
|
985
|
+
--argjson attempt "$next_retry_count" \
|
|
986
|
+
--argjson maxAttempts "$MCC_SEMANTIC_RETRY_LIMIT" \
|
|
987
|
+
--argjson remainingAttempts "$((MCC_SEMANTIC_RETRY_LIMIT - next_retry_count))" \
|
|
988
|
+
--arg reason "$retry_reason" \
|
|
989
|
+
'{attempt: $attempt, maxAttempts: $maxAttempts, remainingAttempts: $remainingAttempts, reason: $reason}') \
|
|
990
|
+
|| mcc_query_failed
|
|
991
|
+
emit_control "MCC_QUERY_RESULT=SUCCESS"
|
|
992
|
+
emit_control "MCC_RESOLUTION=$resolution"
|
|
993
|
+
emit_control "MCC_SEMANTIC_RETRY_REQUIRED=$retry_payload"
|
|
994
|
+
return 0
|
|
995
|
+
fi
|
|
996
|
+
fi
|
|
997
|
+
if [ "$match_type" = "NONE" ]; then
|
|
998
|
+
catalog="$tree_catalog"
|
|
999
|
+
else
|
|
1000
|
+
full_codes=$(printf '%s' "$resolution" | jq -c '.mergedCandidateCodes // []' 2>/dev/null) || mcc_query_failed
|
|
1001
|
+
fi
|
|
1002
|
+
if [ -z "${catalog:-}" ]; then
|
|
1003
|
+
if ! printf '%s' "$full_codes" | jq -e 'type == "array" and length > 0' >/dev/null 2>&1; then
|
|
1004
|
+
catalog="$tree_catalog"
|
|
1005
|
+
else
|
|
1006
|
+
candidate_rows=$(rows_for_full_codes "$rows" "$full_codes") || mcc_query_failed
|
|
1007
|
+
if ! printf '%s' "$candidate_rows" | jq -e 'type == "array" and length > 0' >/dev/null 2>&1; then
|
|
1008
|
+
mcc_query_failed
|
|
1009
|
+
fi
|
|
1010
|
+
qualification_rows=$(printf '%s' "$candidate_rows" | jq -c '[.[] | select(.treeNeedSpecialQual == true)]' 2>/dev/null) || mcc_query_failed
|
|
1011
|
+
if printf '%s' "$qualification_rows" | jq -e 'length > 0' >/dev/null 2>&1; then
|
|
1012
|
+
capture_or_exit details query_mcc_details "$qualification_rows"
|
|
1013
|
+
else
|
|
1014
|
+
details='null'
|
|
1015
|
+
fi
|
|
1016
|
+
catalog=$(normalize_mcc_catalog "$candidate_rows" "$details") || {
|
|
1017
|
+
mcc_query_failed
|
|
1018
|
+
}
|
|
1019
|
+
fi
|
|
1020
|
+
fi
|
|
1021
|
+
fi
|
|
1022
|
+
if [ -n "$agent_candidate_refs_json" ] && ! printf '%s' "$catalog" | jq -e \
|
|
1023
|
+
--argjson refs "$agent_candidate_refs_json" '
|
|
1024
|
+
([.[] | .mccRef?] | map(select(type == "string"))) as $available |
|
|
1025
|
+
($refs | type == "array" and all(.[]; . as $ref | $available | index($ref) != null))
|
|
1026
|
+
' >/dev/null 2>&1; then
|
|
1027
|
+
# An invalid Agent review may have already fallen back to local matches.
|
|
1028
|
+
# Do not pass that invalid reference into the reduced final catalog.
|
|
1029
|
+
agent_candidate_refs_json=""
|
|
1030
|
+
fi
|
|
1031
|
+
node_args=(
|
|
1032
|
+
"$ONBOARDING_RUNNER" mcc-resolve-plan
|
|
1033
|
+
--query "$query"
|
|
1034
|
+
--product-type "$product_type"
|
|
1035
|
+
--root-intent "$root_intent"
|
|
1036
|
+
--mcc-catalog-json "$catalog"
|
|
1037
|
+
--semantic-terms-json "$semantic_terms_json"
|
|
1038
|
+
)
|
|
1039
|
+
if [ -n "$input_id" ]; then
|
|
1040
|
+
node_args+=(--input-id "$input_id")
|
|
1041
|
+
fi
|
|
1042
|
+
if [ -n "$agent_candidate_refs_json" ]; then
|
|
1043
|
+
node_args+=(--agent-candidate-refs-json "$agent_candidate_refs_json")
|
|
1044
|
+
fi
|
|
1045
|
+
if [ "$stale_selection" = true ]; then
|
|
1046
|
+
node_args+=(--selection-state STALE)
|
|
1047
|
+
elif [ -n "$select_value" ] || [ -n "$selection_receipt" ]; then
|
|
1048
|
+
if [ -z "$select_value" ] || [ -z "$selection_receipt" ]; then
|
|
1049
|
+
echo "❌ --select 与 --selection-receipt 必须同时提供"
|
|
1050
|
+
exit 1
|
|
1051
|
+
fi
|
|
1052
|
+
node_args+=(--select "$select_value" --selection-receipt "$selection_receipt")
|
|
1053
|
+
fi
|
|
1054
|
+
local runner_out runner_err runner_rc
|
|
1055
|
+
runner_out=$(mktemp "${TMPDIR:-/tmp}/mcc_runner_out.XXXXXX") || { echo "❌ 创建临时文件失败"; exit 1; }
|
|
1056
|
+
runner_err=$(mktemp "${TMPDIR:-/tmp}/mcc_runner_err.XXXXXX") || { rm -f "$runner_out"; echo "❌ 创建临时文件失败"; exit 1; }
|
|
1057
|
+
"${NODE_BINARY:-node}" "${node_args[@]}" >"$runner_out" 2>"$runner_err"
|
|
1058
|
+
runner_rc=$?
|
|
1059
|
+
if [ "$runner_rc" -ne 0 ]; then
|
|
1060
|
+
rm -f "$runner_out" "$runner_err"
|
|
1061
|
+
mcc_query_failed
|
|
1062
|
+
fi
|
|
1063
|
+
if { [ -z "$selection_receipt" ] || [ "$stale_selection" = true ]; } \
|
|
1064
|
+
&& [ -n "${candidate_rows:-}" ] && [ -n "${details:-}" ]; then
|
|
1065
|
+
local generated_selection_receipt
|
|
1066
|
+
generated_selection_receipt=$(sed -n 's/^ALIPAY_AIPAY_INTERNAL:MCC_SELECTION_RECEIPT=//p' "$runner_err")
|
|
1067
|
+
if [ "$(printf '%s\n' "$generated_selection_receipt" | sed '/^$/d' | wc -l | tr -d ' ')" = "1" ]; then
|
|
1068
|
+
write_mcc_selection_cache "$product_type" "$sales_code" "$query" "$generated_selection_receipt" "$resolved_semantic_terms" "$candidate_rows" "$details"
|
|
1069
|
+
fi
|
|
1070
|
+
fi
|
|
1071
|
+
emit_control "MCC_QUERY_RESULT=SUCCESS"
|
|
1072
|
+
cat "$runner_out"
|
|
1073
|
+
cat "$runner_err" >&2
|
|
1074
|
+
rm -f "$runner_out" "$runner_err"
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
case "${1:-}" in
|
|
1078
|
+
resolve-plan)
|
|
1079
|
+
run_resolve_plan "$@"
|
|
1080
|
+
;;
|
|
1081
|
+
*)
|
|
1082
|
+
echo "用法: bash mcc.sh resolve-plan --query <经营描述|MCC编码> --product-type <aipay|webpay|apppay> --root-intent <onboarding_only|full_process> [--semantic-terms-json <JSON数组>] [--agent-candidate-refs-json <JSON数组>] [--select <MCC编码> --selection-receipt <receipt>] [--input-id <USER_INPUT_ID>]"
|
|
1083
|
+
exit 1
|
|
1084
|
+
;;
|
|
1085
|
+
esac
|