@youzan-cloud/zan-saas-cli 0.0.0-stage → 0.1.0-beta.4
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/DEPENDENCIES.json +50 -0
- package/INSTALL.md +79 -0
- package/LICENSE +21 -0
- package/NOTICE +1 -0
- package/README.md +151 -2
- package/THIRD_PARTY_NOTICES.txt +162 -0
- package/bin/zan-saas-cli.js +28 -0
- package/internal/registry/index.js +123 -0
- package/internal/registry/manifest.json +424 -0
- package/npm-shrinkwrap.json +86 -0
- package/package.json +33 -4
- package/skills/README.md +30 -0
- package/skills/zan-saas/SKILL.md +54 -0
- package/skills/zan-saas/references/host-entry.md +25 -0
- package/skills/zan-saas-access/SKILL.md +37 -0
- package/skills/zan-saas-access/references/role-list.md +11 -0
- package/skills/zan-saas-access/references/staff-perms-get.md +11 -0
- package/skills/zan-saas-aftersale/SKILL.md +31 -0
- package/skills/zan-saas-aftersale/references/case-list.md +13 -0
- package/skills/zan-saas-aftersale/references/case-scan.md +31 -0
- package/skills/zan-saas-benefit/SKILL.md +46 -0
- package/skills/zan-saas-benefit/references/card-get.md +7 -0
- package/skills/zan-saas-benefit/references/card-grant-list.md +11 -0
- package/skills/zan-saas-benefit/references/card-list.md +7 -0
- package/skills/zan-saas-benefit/references/customer-card-get.md +7 -0
- package/skills/zan-saas-benefit/references/customer-card-list.md +7 -0
- package/skills/zan-saas-coupon/SKILL.md +41 -0
- package/skills/zan-saas-coupon/references/activity-get.md +11 -0
- package/skills/zan-saas-coupon/references/activity-list.md +15 -0
- package/skills/zan-saas-coupon/references/voucher-get.md +13 -0
- package/skills/zan-saas-coupon/references/voucher-list.md +25 -0
- package/skills/zan-saas-customer/SKILL.md +73 -0
- package/skills/zan-saas-customer/references/customer-scan.md +11 -0
- package/skills/zan-saas-customer/references/segment-list.md +22 -0
- package/skills/zan-saas-fulfillment/SKILL.md +36 -0
- package/skills/zan-saas-fulfillment/references/delivery-get.md +11 -0
- package/skills/zan-saas-fulfillment/references/shipment-list.md +11 -0
- package/skills/zan-saas-fulfillment/references/waybill-get.md +9 -0
- package/skills/zan-saas-interaction/SKILL.md +37 -0
- package/skills/zan-saas-interaction/references/lottery-get.md +11 -0
- package/skills/zan-saas-interaction/references/lottery-list.md +19 -0
- package/skills/zan-saas-item/SKILL.md +61 -0
- package/skills/zan-saas-item/references/attribute-list.md +11 -0
- package/skills/zan-saas-item/references/brand-list.md +7 -0
- package/skills/zan-saas-item/references/classification-get.md +7 -0
- package/skills/zan-saas-item/references/classification-list.md +7 -0
- package/skills/zan-saas-item/references/group-list.md +9 -0
- package/skills/zan-saas-item/references/item-content.md +9 -0
- package/skills/zan-saas-item/references/item-get.md +9 -0
- package/skills/zan-saas-item/references/item-search.md +23 -0
- package/skills/zan-saas-item/references/product-identity.md +11 -0
- package/skills/zan-saas-member/SKILL.md +41 -0
- package/skills/zan-saas-member/references/customer-level-get.md +7 -0
- package/skills/zan-saas-member/references/growth-log-list.md +17 -0
- package/skills/zan-saas-member/references/membership-get.md +7 -0
- package/skills/zan-saas-order/SKILL.md +56 -0
- package/skills/zan-saas-order/references/order-get.md +26 -0
- package/skills/zan-saas-order/references/order-list.md +25 -0
- package/skills/zan-saas-order/references/order-scan.md +35 -0
- package/skills/zan-saas-org/SKILL.md +37 -0
- package/skills/zan-saas-org/references/staff-get.md +15 -0
- package/skills/zan-saas-org/references/staff-role-get.md +11 -0
- package/skills/zan-saas-org/references/unit-get.md +11 -0
- package/skills/zan-saas-org/references/unit-list.md +15 -0
- package/skills/zan-saas-points/SKILL.md +42 -0
- package/skills/zan-saas-points/references/account-get.md +7 -0
- package/skills/zan-saas-points/references/log-list.md +17 -0
- package/skills/zan-saas-points/references/rule-list.md +2 -0
- package/skills/zan-saas-privacy/SKILL.md +30 -0
- package/skills/zan-saas-privacy/references/field-decrypt.md +19 -0
- package/skills/zan-saas-shared/SKILL.md +94 -0
- package/skills/zan-saas-shared/references/authentication.md +52 -0
- package/skills/zan-saas-shared/references/value-processing.md +27 -0
- package/skills/zan-saas-stock/SKILL.md +28 -0
- package/skills/zan-saas-stock/references/log-list.md +11 -0
- package/skills/zan-saas-valuecard/SKILL.md +37 -0
- package/skills/zan-saas-valuecard/references/card-get.md +13 -0
- package/skills/zan-saas-valuecard/references/card-list.md +11 -0
- package/src/auth/browser.js +28 -0
- package/src/auth/callback-server.js +99 -0
- package/src/auth/config.js +53 -0
- package/src/auth/credential-store.js +109 -0
- package/src/auth/oauth-client.js +105 -0
- package/src/auth/pkce.js +28 -0
- package/src/auth/session.js +178 -0
- package/src/auth/tool.js +25 -0
- package/src/cli/command-paths.js +126 -0
- package/src/cli/context-help.js +102 -0
- package/src/cli/diagnostic-command.js +57 -0
- package/src/cli/domain-catalog.js +42 -0
- package/src/cli/help-text.js +32 -0
- package/src/cli/native-loader.js +120 -0
- package/src/cli/native-path.js +22 -0
- package/src/cli/parser-policy.js +141 -0
- package/src/cli/program.js +134 -0
- package/src/cli/skill-catalog.js +241 -0
- package/src/commands/native/access/commands.index.json +24 -0
- package/src/commands/native/access/domain.json +3 -0
- package/src/commands/native/access/role/list/command.js +141 -0
- package/src/commands/native/access/role/list/schema.json +393 -0
- package/src/commands/native/access/staff-perms/get/command.js +119 -0
- package/src/commands/native/access/staff-perms/get/schema.json +703 -0
- package/src/commands/native/aftersale/case/get/command.js +104 -0
- package/src/commands/native/aftersale/case/get/exact-json.js +52 -0
- package/src/commands/native/aftersale/case/get/money.js +28 -0
- package/src/commands/native/aftersale/case/get/projection.js +120 -0
- package/src/commands/native/aftersale/case/get/query.js +72 -0
- package/src/commands/native/aftersale/case/get/schema.json +134 -0
- package/src/commands/native/aftersale/case/list/command.js +135 -0
- package/src/commands/native/aftersale/case/list/schema.json +1095 -0
- package/src/commands/native/aftersale/case/scan/command.js +110 -0
- package/src/commands/native/aftersale/case/scan/query.js +62 -0
- package/src/commands/native/aftersale/case/scan/scan.js +78 -0
- package/src/commands/native/aftersale/case/scan/schema.json +964 -0
- package/src/commands/native/aftersale/case/scan/search.js +108 -0
- package/src/commands/native/aftersale/commands.index.json +42 -0
- package/src/commands/native/aftersale/domain.json +3 -0
- package/src/commands/native/aftersale/refund/list/command.js +97 -0
- package/src/commands/native/aftersale/refund/list/money.js +25 -0
- package/src/commands/native/aftersale/refund/list/projection.js +50 -0
- package/src/commands/native/aftersale/refund/list/query.js +73 -0
- package/src/commands/native/aftersale/refund/list/schema.json +130 -0
- package/src/commands/native/aftersale/shared/query.js +58 -0
- package/src/commands/native/aftersale/shared/search.js +128 -0
- package/src/commands/native/benefit/card-definition/get/command.js +110 -0
- package/src/commands/native/benefit/card-definition/get/schema.json +545 -0
- package/src/commands/native/benefit/card-definition/list/command.js +106 -0
- package/src/commands/native/benefit/card-definition/list/schema.json +690 -0
- package/src/commands/native/benefit/card-grant/list/command.js +135 -0
- package/src/commands/native/benefit/card-grant/list/schema.json +758 -0
- package/src/commands/native/benefit/commands.index.json +51 -0
- package/src/commands/native/benefit/customer-card/get/command.js +110 -0
- package/src/commands/native/benefit/customer-card/get/schema.json +340 -0
- package/src/commands/native/benefit/customer-card/list/command.js +103 -0
- package/src/commands/native/benefit/customer-card/list/schema.json +731 -0
- package/src/commands/native/benefit/domain.json +3 -0
- package/src/commands/native/coupon/activity/get/command.js +183 -0
- package/src/commands/native/coupon/activity/get/schema.json +624 -0
- package/src/commands/native/coupon/activity/list/command.js +169 -0
- package/src/commands/native/coupon/activity/list/schema.json +845 -0
- package/src/commands/native/coupon/commands.index.json +42 -0
- package/src/commands/native/coupon/domain.json +3 -0
- package/src/commands/native/coupon/shared/query.js +15 -0
- package/src/commands/native/coupon/voucher/get/command.js +113 -0
- package/src/commands/native/coupon/voucher/get/schema.json +147 -0
- package/src/commands/native/coupon/voucher/list/command.js +155 -0
- package/src/commands/native/coupon/voucher/list/schema.json +772 -0
- package/src/commands/native/customer/commands.index.json +96 -0
- package/src/commands/native/customer/customer/get/adapter.js +82 -0
- package/src/commands/native/customer/customer/get/command.js +106 -0
- package/src/commands/native/customer/customer/get/schema.json +958 -0
- package/src/commands/native/customer/customer/list/command.js +78 -0
- package/src/commands/native/customer/customer/list/input.js +145 -0
- package/src/commands/native/customer/customer/list/query.js +103 -0
- package/src/commands/native/customer/customer/list/schema.json +969 -0
- package/src/commands/native/customer/customer/scan/command.js +80 -0
- package/src/commands/native/customer/customer/scan/scan.js +92 -0
- package/src/commands/native/customer/customer/scan/schema.json +211 -0
- package/src/commands/native/customer/customer-tag/get/command.js +166 -0
- package/src/commands/native/customer/customer-tag/get/schema.json +317 -0
- package/src/commands/native/customer/customer-tag/list/command.js +143 -0
- package/src/commands/native/customer/customer-tag/list/schema.json +259 -0
- package/src/commands/native/customer/domain.json +3 -0
- package/src/commands/native/customer/segment/list/command.js +96 -0
- package/src/commands/native/customer/segment/list/query.js +98 -0
- package/src/commands/native/customer/segment/list/schema.json +135 -0
- package/src/commands/native/customer/segment-customer/list/command.js +171 -0
- package/src/commands/native/customer/segment-customer/list/schema.json +123 -0
- package/src/commands/native/customer/shared/search-input.js +179 -0
- package/src/commands/native/customer/shared/search-page.js +54 -0
- package/src/commands/native/customer/tag/get/command.js +73 -0
- package/src/commands/native/customer/tag/get/schema.json +339 -0
- package/src/commands/native/customer/tagcategory/get/command.js +80 -0
- package/src/commands/native/customer/tagcategory/get/query.js +98 -0
- package/src/commands/native/customer/tagcategory/get/schema.json +339 -0
- package/src/commands/native/customer/tagcategory/list/command.js +127 -0
- package/src/commands/native/customer/tagcategory/list/schema.json +394 -0
- package/src/commands/native/fulfillment/commands.index.json +33 -0
- package/src/commands/native/fulfillment/delivery/get/command.js +107 -0
- package/src/commands/native/fulfillment/delivery/get/projection.js +142 -0
- package/src/commands/native/fulfillment/delivery/get/schema.json +173 -0
- package/src/commands/native/fulfillment/domain.json +3 -0
- package/src/commands/native/fulfillment/shipment/list/command.js +83 -0
- package/src/commands/native/fulfillment/shipment/list/projection.js +130 -0
- package/src/commands/native/fulfillment/shipment/list/schema.json +752 -0
- package/src/commands/native/fulfillment/waybill/get/command.js +193 -0
- package/src/commands/native/fulfillment/waybill/get/response.js +68 -0
- package/src/commands/native/fulfillment/waybill/get/schema.json +190 -0
- package/src/commands/native/interaction/commands.index.json +24 -0
- package/src/commands/native/interaction/domain.json +3 -0
- package/src/commands/native/interaction/lottery/get/command.js +61 -0
- package/src/commands/native/interaction/lottery/get/query.js +152 -0
- package/src/commands/native/interaction/lottery/get/schema.json +1004 -0
- package/src/commands/native/interaction/lottery/list/command.js +176 -0
- package/src/commands/native/interaction/lottery/list/schema.json +898 -0
- package/src/commands/native/item/attribute/list/command.js +122 -0
- package/src/commands/native/item/attribute/list/schema.json +727 -0
- package/src/commands/native/item/brand/list/command.js +114 -0
- package/src/commands/native/item/brand/list/schema.json +725 -0
- package/src/commands/native/item/classification/get/command.js +87 -0
- package/src/commands/native/item/classification/get/schema.json +596 -0
- package/src/commands/native/item/classification/list/command.js +105 -0
- package/src/commands/native/item/classification/list/schema.json +676 -0
- package/src/commands/native/item/commands.index.json +87 -0
- package/src/commands/native/item/domain.json +1 -0
- package/src/commands/native/item/group/list/command.js +122 -0
- package/src/commands/native/item/group/list/schema.json +809 -0
- package/src/commands/native/item/item/get/command.js +122 -0
- package/src/commands/native/item/item/get/schema.json +3799 -0
- package/src/commands/native/item/item/list/command.js +7 -0
- package/src/commands/native/item/item/list/schema.json +1118 -0
- package/src/commands/native/item/item/scan/command.js +7 -0
- package/src/commands/native/item/item/scan/schema.json +1046 -0
- package/src/commands/native/item/item-content/get/command.js +54 -0
- package/src/commands/native/item/item-content/get/schema.json +677 -0
- package/src/commands/native/item/shared/api.js +31 -0
- package/src/commands/native/item/shared/content.js +20 -0
- package/src/commands/native/item/shared/diagnostics.js +61 -0
- package/src/commands/native/item/shared/h5.js +63 -0
- package/src/commands/native/item/shared/identity.js +62 -0
- package/src/commands/native/item/shared/normalize.js +49 -0
- package/src/commands/native/item/shared/scope.js +22 -0
- package/src/commands/native/item/shared/search-command.js +61 -0
- package/src/commands/native/item/shared/search-options.js +150 -0
- package/src/commands/native/item/shared/search.js +158 -0
- package/src/commands/native/member/commands.index.json +60 -0
- package/src/commands/native/member/customer-level/get/command.js +122 -0
- package/src/commands/native/member/customer-level/get/schema.json +300 -0
- package/src/commands/native/member/domain.json +3 -0
- package/src/commands/native/member/growth/get/command.js +64 -0
- package/src/commands/native/member/growth/get/schema.json +283 -0
- package/src/commands/native/member/growth-log/list/command.js +203 -0
- package/src/commands/native/member/growth-log/list/schema.json +695 -0
- package/src/commands/native/member/level/get/command.js +70 -0
- package/src/commands/native/member/level/get/query.js +125 -0
- package/src/commands/native/member/level/get/schema.json +339 -0
- package/src/commands/native/member/level/list/command.js +50 -0
- package/src/commands/native/member/level/list/query.js +109 -0
- package/src/commands/native/member/level/list/schema.json +104 -0
- package/src/commands/native/member/membership/get/command.js +78 -0
- package/src/commands/native/member/membership/get/schema.json +283 -0
- package/src/commands/native/order/commands.index.json +33 -0
- package/src/commands/native/order/domain.json +3 -0
- package/src/commands/native/order/order/get/command.js +155 -0
- package/src/commands/native/order/order/get/schema.json +1167 -0
- package/src/commands/native/order/order/list/command.js +230 -0
- package/src/commands/native/order/order/list/schema.json +1363 -0
- package/src/commands/native/order/order/scan/command.js +148 -0
- package/src/commands/native/order/order/scan/scan.js +89 -0
- package/src/commands/native/order/order/scan/schema.json +1445 -0
- package/src/commands/native/order/shared/projection.js +183 -0
- package/src/commands/native/order/shared/query.js +84 -0
- package/src/commands/native/order/shared/search.js +112 -0
- package/src/commands/native/org/commands.index.json +42 -0
- package/src/commands/native/org/domain.json +3 -0
- package/src/commands/native/org/shared/single-staff.js +115 -0
- package/src/commands/native/org/staff/get/command.js +148 -0
- package/src/commands/native/org/staff/get/schema.json +114 -0
- package/src/commands/native/org/staff-role/get/command.js +45 -0
- package/src/commands/native/org/staff-role/get/schema.json +93 -0
- package/src/commands/native/org/unit/get/command.js +131 -0
- package/src/commands/native/org/unit/get/schema.json +478 -0
- package/src/commands/native/org/unit/list/command.js +187 -0
- package/src/commands/native/org/unit/list/schema.json +828 -0
- package/src/commands/native/points/account/get/command.js +65 -0
- package/src/commands/native/points/account/get/schema.json +303 -0
- package/src/commands/native/points/commands.index.json +33 -0
- package/src/commands/native/points/domain.json +3 -0
- package/src/commands/native/points/log/list/command.js +141 -0
- package/src/commands/native/points/log/list/schema.json +761 -0
- package/src/commands/native/points/rule/list/command.js +82 -0
- package/src/commands/native/points/rule/list/schema.json +294 -0
- package/src/commands/native/privacy/commands.index.json +15 -0
- package/src/commands/native/privacy/domain.json +3 -0
- package/src/commands/native/privacy/field/decrypt/command.js +185 -0
- package/src/commands/native/privacy/field/decrypt/schema.json +656 -0
- package/src/commands/native/privacy/shared/order-source.js +33 -0
- package/src/commands/native/stock/commands.index.json +15 -0
- package/src/commands/native/stock/domain.json +3 -0
- package/src/commands/native/stock/log/list/command.js +207 -0
- package/src/commands/native/stock/log/list/schema.json +922 -0
- package/src/commands/native/valuecard/card/get/command.js +107 -0
- package/src/commands/native/valuecard/card/get/schema.json +683 -0
- package/src/commands/native/valuecard/card/list/command.js +136 -0
- package/src/commands/native/valuecard/card/list/pagination.js +23 -0
- package/src/commands/native/valuecard/card/list/schema.json +780 -0
- package/src/commands/native/valuecard/commands.index.json +24 -0
- package/src/commands/native/valuecard/domain.json +3 -0
- package/src/commands/native/valuecard/shared/projection.js +57 -0
- package/src/commands/system/api.js +47 -0
- package/src/commands/system/auth.js +118 -0
- package/src/commands/system/contracts.js +373 -0
- package/src/commands/system/doctor.js +24 -0
- package/src/commands/system/install.js +41 -0
- package/src/commands/system/printer.js +16 -0
- package/src/commands/system/schema.js +162 -0
- package/src/commands/system/shop-guidance.js +9 -0
- package/src/commands/system/shop.js +237 -0
- package/src/commands/system/skills.js +49 -0
- package/src/execution/beijing-date-time.js +29 -0
- package/src/execution/beijing-time.js +21 -0
- package/src/execution/bounded-input.js +42 -0
- package/src/execution/command-io.js +137 -0
- package/src/execution/command-policy.js +41 -0
- package/src/execution/command-schema.js +131 -0
- package/src/execution/datetime.js +83 -0
- package/src/execution/error-details.js +399 -0
- package/src/execution/error-model.js +82 -0
- package/src/execution/exact-json.js +80 -0
- package/src/execution/list-total.js +10 -0
- package/src/execution/money.js +59 -0
- package/src/execution/parser-error.js +66 -0
- package/src/execution/privacy.js +197 -0
- package/src/execution/public-schema.js +52 -0
- package/src/execution/query-api.js +101 -0
- package/src/execution/range.js +84 -0
- package/src/execution/request-state.js +55 -0
- package/src/execution/schema-completeness.js +116 -0
- package/src/execution/shop-query.js +54 -0
- package/src/execution/target-shop-default.js +23 -0
- package/src/execution/target-shop.js +49 -0
- package/src/identity/context-store.js +54 -0
- package/src/identity/context.js +38 -0
- package/src/installation/install.js +158 -0
- package/src/installation/npm-error.js +93 -0
- package/src/installation/skill.js +113 -0
- package/src/transport/account-api.js +152 -0
- package/src/transport/api.js +199 -0
- package/src/transport/shop-app.js +63 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { apiSeconds, parseDateTime, formatDateTime } = require('../../../../../execution/datetime');
|
|
4
|
+
const { object } = require('../../../../../execution/query-api');
|
|
5
|
+
const { reject } = require('./query');
|
|
6
|
+
|
|
7
|
+
const listPath = 'youzan.trade.refund.search/3.0.1';
|
|
8
|
+
const filterOptions = [
|
|
9
|
+
['orderNo', 'order-no', 'tid'], ['caseId', 'case-id', 'refund_id'],
|
|
10
|
+
['yzOpenId', 'yz-open-id', 'yz_open_id'], ['buyerPhone', 'buyer-phone', 'buyer_phone'],
|
|
11
|
+
['demand', 'demand', 'demand'], ['status', 'status', 'status'], ['type', 'type', 'type'],
|
|
12
|
+
['phase', 'phase', 'phase'], ['handlingStatus', 'handling-status', 'search_tag'],
|
|
13
|
+
['serviceStatus', 'service-status', 'cs_status'], ['itemTitle', 'item-title', 'goods_title'],
|
|
14
|
+
['skuNo', 'sku-no', 'sku_no'], ['deliveryStatus', 'delivery-status', 'delivery_status'],
|
|
15
|
+
['refundMode', 'refund-mode', 'refund_mode'], ['returnStockStatus', 'return-stock-status', 'return_stock_status'],
|
|
16
|
+
['saleWay', 'sale-way', 'sale_way'], ['payType', 'pay-type', 'pay_type']
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function toRequestBody(input, pageNo = 1) {
|
|
20
|
+
const body = { page_no: pageNo, page_size: Math.min(20, input.limits.maxItems) };
|
|
21
|
+
for (const [name, , upstream] of filterOptions) {
|
|
22
|
+
if (Object.hasOwn(input.filters, name)) body[upstream] = input.filters[name];
|
|
23
|
+
}
|
|
24
|
+
for (const [name, upstream] of [['created', 'create'], ['updated', 'update']]) {
|
|
25
|
+
if (!input.range[name]) continue;
|
|
26
|
+
body[`${upstream}_time_start`] = apiSeconds(input.range[name].start);
|
|
27
|
+
body[`${upstream}_time_end`] = apiSeconds(input.range[name].end);
|
|
28
|
+
}
|
|
29
|
+
return body;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function toPreviewBody(input) {
|
|
33
|
+
const body = toRequestBody(input);
|
|
34
|
+
for (const field of ['yz_open_id', 'buyer_phone', 'goods_title']) {
|
|
35
|
+
if (Object.hasOwn(body, field)) body[field] = '***';
|
|
36
|
+
}
|
|
37
|
+
return body;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function text(value) {
|
|
41
|
+
if (typeof value !== 'string' || !value || value.trim() !== value || /[\s\p{Cc}]/u.test(value)) {
|
|
42
|
+
throw new Error('invalid identifier');
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function positiveId(value) {
|
|
48
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new Error('invalid shop');
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function amount(value) {
|
|
53
|
+
if (typeof value !== 'string' || !/^(0|[1-9]\d*)(\.\d+)?$/.test(value)) {
|
|
54
|
+
throw new Error('invalid amount');
|
|
55
|
+
}
|
|
56
|
+
const [yuan, fraction = ''] = value.split('.');
|
|
57
|
+
if (/[1-9]/.test(fraction.slice(2))) throw new Error('inexact amount');
|
|
58
|
+
const cents = BigInt(yuan) * 100n + BigInt(fraction.slice(0, 2).padEnd(2, '0'));
|
|
59
|
+
if (cents > 9223372036854775807n) throw new Error('amount overflow');
|
|
60
|
+
return `${cents / 100n}.${String(cents % 100n).padStart(2, '0')}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function projectCase(source, scope, facts) {
|
|
64
|
+
if (!object(source)) throw new Error('invalid row');
|
|
65
|
+
const row = {
|
|
66
|
+
caseId: text(source.refund_id), orderNo: text(source.tid),
|
|
67
|
+
kdtId: positiveId(source.kdt_id), status: text(source.status), amount: amount(source.refund_fee)
|
|
68
|
+
};
|
|
69
|
+
if (row.kdtId !== scope.kdtId) throw reject('scope_unresolved', 'cases', scope, 'sent', facts);
|
|
70
|
+
if (Object.hasOwn(source, 'node_kdt_id')) row.nodeKdtId = positiveId(source.node_kdt_id);
|
|
71
|
+
if (Object.hasOwn(source, 'return_goods')) {
|
|
72
|
+
if (typeof source.return_goods !== 'boolean') throw new Error('invalid return flag');
|
|
73
|
+
row.hasReturnGoods = source.return_goods;
|
|
74
|
+
}
|
|
75
|
+
for (const [key, upstream] of [['createdAt', 'created'], ['updatedAt', 'modified']]) {
|
|
76
|
+
if (Object.hasOwn(source, upstream)) row[key] = formatDateTime(parseDateTime(source[upstream]));
|
|
77
|
+
}
|
|
78
|
+
for (const [key, upstream] of [['serviceStatus', 'cs_status'], ['deliveryStatus', 'delivery_status']]) {
|
|
79
|
+
if (!Object.hasOwn(source, upstream)) continue;
|
|
80
|
+
if (!Number.isSafeInteger(source[upstream])) throw new Error('invalid status');
|
|
81
|
+
row[key] = source[upstream];
|
|
82
|
+
}
|
|
83
|
+
return row;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function adaptPage(data, input, pageNo, facts = {}) {
|
|
87
|
+
try {
|
|
88
|
+
const pageSize = Math.min(20, input.limits.maxItems);
|
|
89
|
+
if (!object(data) || !Array.isArray(data.refunds) || data.refunds.length > pageSize) {
|
|
90
|
+
throw new Error('invalid page');
|
|
91
|
+
}
|
|
92
|
+
const total = Object.hasOwn(data, 'total') ? data.total : null;
|
|
93
|
+
if (Object.hasOwn(data, 'total') && (!Number.isSafeInteger(total) || total < 0)) {
|
|
94
|
+
throw new Error('invalid total');
|
|
95
|
+
}
|
|
96
|
+
const offset = (pageNo - 1) * pageSize;
|
|
97
|
+
if (total !== null && ((data.refunds.length > 0 && offset + data.refunds.length > total) ||
|
|
98
|
+
(offset < total && data.refunds.length === 0))) throw new Error('contradictory page');
|
|
99
|
+
const cases = data.refunds.map(source => projectCase(source, input.scope, facts));
|
|
100
|
+
if (new Set(cases.map(row => row.caseId)).size !== cases.length) throw new Error('page duplicates');
|
|
101
|
+
return { cases, total };
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error.cliDetails) throw error;
|
|
104
|
+
throw reject('protocol', 'output-model', input.scope, 'sent', facts);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = { listPath, filterOptions, toRequestBody, toPreviewBody, adaptPage, amount };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 3,
|
|
3
|
+
"domain": "aftersale",
|
|
4
|
+
"businessCommands": [
|
|
5
|
+
{
|
|
6
|
+
"path": "aftersale.case.get",
|
|
7
|
+
"kind": "atomic",
|
|
8
|
+
"risk": "read",
|
|
9
|
+
"availability": "available",
|
|
10
|
+
"at": "shop",
|
|
11
|
+
"module": "aftersale/case/get/command.js",
|
|
12
|
+
"fingerprint": "c9e21d47c9da5eea5590a057dfe410ea08279eaaf75280ab4dc9916913ab8308"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"path": "aftersale.case.list",
|
|
16
|
+
"kind": "atomic",
|
|
17
|
+
"risk": "read",
|
|
18
|
+
"availability": "available",
|
|
19
|
+
"at": "shop",
|
|
20
|
+
"module": "aftersale/case/list/command.js",
|
|
21
|
+
"fingerprint": "978c490a65bad5c348f700aa5b900604876ba3a7833207f677c35cf9b486d375"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "aftersale.case.scan",
|
|
25
|
+
"kind": "atomic",
|
|
26
|
+
"risk": "read",
|
|
27
|
+
"availability": "available",
|
|
28
|
+
"at": "shop",
|
|
29
|
+
"module": "aftersale/case/scan/command.js",
|
|
30
|
+
"fingerprint": "7b5fae46b975e3dc77d67751da87f3be22cac491770c0d9f3cd97126e78ec215"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"path": "aftersale.refund.list",
|
|
34
|
+
"kind": "atomic",
|
|
35
|
+
"risk": "read",
|
|
36
|
+
"availability": "available",
|
|
37
|
+
"at": "shop",
|
|
38
|
+
"module": "aftersale/refund/list/command.js",
|
|
39
|
+
"fingerprint": "f010665436b41eacd7d4d54c98b0a8d811a5e2a96ec722663af4bc16588a6e4e"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { withTargetShopDefault } = require('../../../../../execution/target-shop-default');
|
|
4
|
+
|
|
5
|
+
const { Command, Option } = require('commander');
|
|
6
|
+
const { validateCommandValue } = require('../../../../../execution/command-schema');
|
|
7
|
+
const { bindingPath, targetScope, createQuery, reject, serialize } = require('./query');
|
|
8
|
+
const { caseSummary, refunds, identifier } = require('./projection');
|
|
9
|
+
const schema = require('./schema.json');
|
|
10
|
+
|
|
11
|
+
const command = 'aftersale.refund.list';
|
|
12
|
+
|
|
13
|
+
function buildInput(options, context) {
|
|
14
|
+
const target = targetScope(options, context, command);
|
|
15
|
+
let caseId;
|
|
16
|
+
try { caseId = identifier(options.caseId); }
|
|
17
|
+
catch { throw reject(command, 'validation', 'input', target); }
|
|
18
|
+
const input = { scope: Object.freeze({ ...target, caseId }) };
|
|
19
|
+
validateCommandValue(schema, 'input', input);
|
|
20
|
+
return input;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function preview(input) {
|
|
24
|
+
const result = { requests: [
|
|
25
|
+
{ method: 'POST', path: bindingPath, body: {} },
|
|
26
|
+
{ method: 'POST', path: schema.api.path, body: { refund_id: input.scope.caseId } }
|
|
27
|
+
] };
|
|
28
|
+
validateCommandValue(schema, 'preview', result);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function adapt(data, scope, facts) {
|
|
33
|
+
let parent;
|
|
34
|
+
try { parent = caseSummary(data); }
|
|
35
|
+
catch { throw reject(command, 'protocol', 'case', scope, 'sent', facts); }
|
|
36
|
+
if (parent.caseId !== scope.caseId || parent.kdtId !== scope.kdtId) {
|
|
37
|
+
throw reject(command, 'scope_unresolved', 'case', scope, 'sent', facts);
|
|
38
|
+
}
|
|
39
|
+
let funds;
|
|
40
|
+
try { funds = refunds(data.refund_fund_list); }
|
|
41
|
+
catch { throw reject(command, 'protocol', 'refunds', scope, 'sent', facts); }
|
|
42
|
+
const output = { data: { case: parent, refunds: funds } };
|
|
43
|
+
try { validateCommandValue(schema, 'output', output); }
|
|
44
|
+
catch { throw reject(command, 'protocol', 'output-model', scope, 'sent', facts); }
|
|
45
|
+
return output;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function single(value, previous) {
|
|
49
|
+
if (previous !== undefined) throw reject(command, 'validation', 'input');
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createCommand({ context } = {}) {
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
function once(name) {
|
|
56
|
+
if (seen.has(name)) throw reject(command, 'validation', 'input');
|
|
57
|
+
seen.add(name);
|
|
58
|
+
}
|
|
59
|
+
return new Command('list')
|
|
60
|
+
.description('查询已知售后单本次返回的逐笔退款资金记录,不办理退款或承诺到账')
|
|
61
|
+
.allowExcessArguments(false)
|
|
62
|
+
.exitOverride(error => {
|
|
63
|
+
if (['commander.helpDisplayed', 'commander.version'].includes(error.code)) throw error;
|
|
64
|
+
throw reject(command, 'validation', 'input');
|
|
65
|
+
})
|
|
66
|
+
.option('--kdt-id <id>', '目标店铺正安全整数;省略时使用当前上下文店铺', single)
|
|
67
|
+
.requiredOption('--case-id <id>', '必填:父售后号,1..128 字符,无空白或控制字符', single)
|
|
68
|
+
.addOption(new Option('--format <format>', 'json 或 pretty,仅改变缩进').choices(['json', 'pretty']).default('json'))
|
|
69
|
+
.option('--dry-run', '纯本地预览,不读凭证、不联网')
|
|
70
|
+
.on('option:format', () => once('format'))
|
|
71
|
+
.on('option:dry-run', () => once('dry-run'))
|
|
72
|
+
.addHelpText('after', `
|
|
73
|
+
执行需要有效店铺四字段上下文,显式店铺须等于 context.shopId;总部拒绝。
|
|
74
|
+
先核验凭证绑定店铺,再读父售后,最多两请求;预览不证明权限或对象存在。
|
|
75
|
+
只返回 data.case 与 data.refunds,无分页,不保证全生命周期全集。
|
|
76
|
+
缺失或 null 资金数组失败,明确空数组才输出 [];坏行或重复流水号整体失败。
|
|
77
|
+
售后号、资金流水号和资金元素退款 ID 不互换;售后成功不能证明资金到账。
|
|
78
|
+
资金金额默认 CNY,分整数精确转两位元字符串;不求和、不四舍五入。
|
|
79
|
+
资金状态 0 退款中、1 失败、2 成功,未知安全整数保留;模式 3 仅保留原码。
|
|
80
|
+
累计响应上限 10MiB,最终 UTF-8 JSON 含换行上限 5MiB,超限取消、不裁切。
|
|
81
|
+
不重试、重定向、换店、换身份或发起退款;失败 stdout 空、stderr 一份安全 JSON。
|
|
82
|
+
执行前请登录并选择店铺,或由受控环境提供上下文和凭证。
|
|
83
|
+
示例:zan-saas aftersale refund list --kdt-id 63077 --case-id example-case --dry-run
|
|
84
|
+
`)
|
|
85
|
+
.action(async options => {
|
|
86
|
+
const input = buildInput(options, context);
|
|
87
|
+
if (options.dryRun) {
|
|
88
|
+
console.log(JSON.stringify(preview(input), null, options.format === 'pretty' ? 2 : 0));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const read = await createQuery(input.scope, command);
|
|
92
|
+
const { data, facts } = await read(schema.api.path, { refund_id: input.scope.caseId }, 'case');
|
|
93
|
+
console.log(serialize(adapt(data, input.scope, facts), options.format, command, input.scope));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { kind: 'atomic', risk: 'read', at: 'shop', createCommand: withTargetShopDefault(createCommand), buildInput, preview, adapt };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { parseExactJson } = require('../../../../../execution/exact-json');
|
|
4
|
+
const maxCents = 9223372036854775807n;
|
|
5
|
+
|
|
6
|
+
function decodeEnvelope(rawBody) {
|
|
7
|
+
return parseExactJson(rawBody, literal => {
|
|
8
|
+
if (/^-?\d{16,19}$/.test(literal) && !Number.isSafeInteger(Number(literal))) return BigInt(literal);
|
|
9
|
+
return parseExactJson(literal);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function centsToYuan(value) {
|
|
14
|
+
if (typeof value === 'number') {
|
|
15
|
+
if (!Number.isSafeInteger(value) || Object.is(value, -0)) throw new Error('invalid cents');
|
|
16
|
+
value = BigInt(value);
|
|
17
|
+
} else if (typeof value === 'string') {
|
|
18
|
+
if (!/^(?:0|[1-9]\d{0,18})$/.test(value)) throw new Error('invalid cents');
|
|
19
|
+
value = BigInt(value);
|
|
20
|
+
}
|
|
21
|
+
if (typeof value !== 'bigint' || value < 0n || value > maxCents) throw new Error('invalid cents');
|
|
22
|
+
return `${value / 100n}.${String(value % 100n).padStart(2, '0')}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { centsToYuan, decodeEnvelope };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { object } = require('../../../../../execution/query-api');
|
|
4
|
+
const { centsToYuan } = require('./money');
|
|
5
|
+
|
|
6
|
+
function text(value, nonempty = false) {
|
|
7
|
+
if (typeof value !== 'string' || (nonempty && !value.trim())) throw new Error('invalid text');
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function identifier(value) {
|
|
12
|
+
if (typeof value !== 'string' || !/^[^\s\u0000-\u001f\u007f-\u009f]{1,128}$/u.test(value)) {
|
|
13
|
+
throw new Error('invalid identifier');
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function integer(value) {
|
|
19
|
+
if (!Number.isSafeInteger(value)) throw new Error('invalid integer');
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function caseSummary(source) {
|
|
24
|
+
if (!object(source)) throw new Error('missing case');
|
|
25
|
+
const kdtId = integer(source.kdt_id);
|
|
26
|
+
if (kdtId < 1) throw new Error('invalid shop');
|
|
27
|
+
return { caseId: identifier(source.refund_id), orderNo: text(source.tid, true), kdtId,
|
|
28
|
+
demand: integer(source.demand), status: text(source.status, true) };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function refunds(source) {
|
|
32
|
+
if (!Array.isArray(source)) throw new Error('missing funds');
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
return source.map(entry => {
|
|
35
|
+
if (!object(entry)) throw new Error('invalid fund');
|
|
36
|
+
const refundNo = identifier(entry.refund_no);
|
|
37
|
+
if (seen.has(refundNo)) throw new Error('duplicate fund');
|
|
38
|
+
seen.add(refundNo);
|
|
39
|
+
const result = { refundNo, amount: centsToYuan(entry.refund_fee), status: integer(entry.status) };
|
|
40
|
+
for (const [target, field, convert] of [
|
|
41
|
+
['refundId', 'refund_id', value => text(value, true)], ['mode', 'refund_mode', integer],
|
|
42
|
+
['message', 'message', text], ['payWayText', 'pay_way_desc', text], ['payWayLabel', 'pay_way_str', text]
|
|
43
|
+
]) {
|
|
44
|
+
if (Object.hasOwn(entry, field)) result[target] = convert(entry[field]);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = { caseSummary, refunds, identifier };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { resolveTargetShop } = require('../../../../../execution/target-shop');
|
|
4
|
+
const { readContext } = require('../../../../../identity/context');
|
|
5
|
+
const { readApiEnvelope, assertApiSuccess, object } = require('../../../../../execution/query-api');
|
|
6
|
+
const { failure, bindingPath } = require('../../../../../execution/shop-query');
|
|
7
|
+
const { snapshotApiEnvironment } = require('../../../../../transport/api');
|
|
8
|
+
const { decodeEnvelope } = require('./money');
|
|
9
|
+
|
|
10
|
+
const RESPONSE_LIMIT = 10 * 1024 * 1024;
|
|
11
|
+
const OUTPUT_LIMIT = 5 * 1024 * 1024;
|
|
12
|
+
|
|
13
|
+
function reject(command, category, stage, scope = {}, requestState = 'not_sent', facts = {}) {
|
|
14
|
+
const error = failure(command, category, stage, scope, requestState, facts);
|
|
15
|
+
error.cliDetails.nextStep = stage === 'shop-binding' ?
|
|
16
|
+
'父售后请求尚未发送。核对原凭证绑定店铺与显式目标,不自动重试、切店或换身份。' :
|
|
17
|
+
category === 'limit' ? '结果超出字节限制,未输出部分资金;停止并核对原范围,不截断或自动重试。' :
|
|
18
|
+
'核对原店铺、售后单号、上下文与服务端授权;不自动重试、切店、换身份或发起退款。';
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function targetScope(options, context, command) {
|
|
23
|
+
let scope;
|
|
24
|
+
try { scope = resolveTargetShop(options, context, command); }
|
|
25
|
+
catch (error) {
|
|
26
|
+
const details = error.cliDetails;
|
|
27
|
+
if (!details) throw error;
|
|
28
|
+
throw reject(command, details.category, ['validation', 'scope'].includes(details.stage) ? 'input' : details.stage,
|
|
29
|
+
details.targetScope);
|
|
30
|
+
}
|
|
31
|
+
const state = readContext(context);
|
|
32
|
+
if (state.status === 'ready' && state.context.orgType !== 'shop') {
|
|
33
|
+
throw reject(command, 'permission', 'context', scope);
|
|
34
|
+
}
|
|
35
|
+
return scope;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function createQuery(scope, command, { responseLimit = RESPONSE_LIMIT } = {}) {
|
|
39
|
+
let env;
|
|
40
|
+
try { env = await snapshotApiEnvironment(undefined, scope.kdtId); }
|
|
41
|
+
catch { throw reject(command, 'configuration', 'configuration', scope); }
|
|
42
|
+
let responseBytes = 0;
|
|
43
|
+
async function read(path, body, stage) {
|
|
44
|
+
const { result, rawBody, facts } = await readApiEnvelope(path, body,
|
|
45
|
+
(category, details, state, at) => reject(command, category,
|
|
46
|
+
['configuration', 'transport', 'response'].includes(at) ? at : stage, scope, state, details), env,
|
|
47
|
+
response => { responseBytes += response.responseBytes; },
|
|
48
|
+
{ maxResponseBytes: () => responseLimit - responseBytes });
|
|
49
|
+
assertApiSuccess(result, category => {
|
|
50
|
+
const code = String(facts.businessCode);
|
|
51
|
+
const mapped = category === 'business_rejected' && stage === 'case' && !Object.hasOwn(result, 'gw_err_resp') ?
|
|
52
|
+
code === '103010054' ? 'permission' : stage === 'case' && code === '103011024' ? 'not_found' : category : category;
|
|
53
|
+
throw reject(command, mapped, stage, scope, 'sent', facts);
|
|
54
|
+
}, [200, '200']);
|
|
55
|
+
return { data: decodeEnvelope(rawBody).data, facts };
|
|
56
|
+
}
|
|
57
|
+
const binding = await read(bindingPath, {}, 'shop-binding');
|
|
58
|
+
if (!object(binding.data) || !Number.isSafeInteger(binding.data.sid) || binding.data.sid < 1) {
|
|
59
|
+
throw reject(command, 'protocol', 'shop-binding', scope, 'sent', binding.facts);
|
|
60
|
+
}
|
|
61
|
+
if (binding.data.sid !== scope.kdtId) throw reject(command, 'scope_unresolved', 'shop-binding', scope, 'sent', binding.facts);
|
|
62
|
+
return read;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function serialize(output, format, command, scope, outputLimit = OUTPUT_LIMIT) {
|
|
66
|
+
const json = JSON.stringify(output, null, format === 'pretty' ? 2 : 0);
|
|
67
|
+
if (Buffer.byteLength(`${json}\n`, 'utf8') > outputLimit) {
|
|
68
|
+
throw reject(command, 'limit', 'output-model', scope, 'sent');
|
|
69
|
+
}
|
|
70
|
+
return json;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = { bindingPath, targetScope, createQuery, reject, serialize, RESPONSE_LIMIT, OUTPUT_LIMIT };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"api": { "method": "POST", "path": "youzan.trade.refund.get/3.0.1" },
|
|
4
|
+
"input": {
|
|
5
|
+
"type": "object", "required": ["scope"], "additionalProperties": false,
|
|
6
|
+
"description": "解析后的业务输入;format 与 dry-run 是展示和执行选项,不进入业务 input。",
|
|
7
|
+
"properties": {
|
|
8
|
+
"scope": {
|
|
9
|
+
"type": "object", "required": ["kdtId", "caseId"], "additionalProperties": false,
|
|
10
|
+
"properties": { "kdtId": { "$ref": "#/definitions/shopId" }, "caseId": { "$ref": "#/definitions/identifier" } }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"output": {
|
|
15
|
+
"type": "object", "required": ["data"], "additionalProperties": false,
|
|
16
|
+
"properties": {
|
|
17
|
+
"data": {
|
|
18
|
+
"type": "object", "required": ["case", "refunds"], "additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"case": {
|
|
21
|
+
"type": "object", "required": ["caseId", "orderNo", "kdtId", "demand", "status"], "additionalProperties": false,
|
|
22
|
+
"description": "核验同店且与输入父售后号一致的售后摘要,不代表资金到账。",
|
|
23
|
+
"properties": {
|
|
24
|
+
"caseId": { "$ref": "#/definitions/identifier", "description": "父售后号,不是资金流水号或资金元素退款 ID。" },
|
|
25
|
+
"orderNo": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "父售后关联的交易订单号。" },
|
|
26
|
+
"kdtId": { "$ref": "#/definitions/shopId" },
|
|
27
|
+
"demand": {
|
|
28
|
+
"$ref": "#/definitions/safeInteger",
|
|
29
|
+
"description": "售后诉求:1 仅退款;2 退货退款;3 换货。未来未知安全整数保留,不推断流程。"
|
|
30
|
+
},
|
|
31
|
+
"status": {
|
|
32
|
+
"type": "string", "minLength": 1, "pattern": "\\S",
|
|
33
|
+
"description": "售后状态:WAIT_SELLER_AGREE 买家已申请、等待卖家同意;WAIT_BUYER_RETURN_GOODS 卖家已同意、等待买家退货;WAIT_SELLER_CONFIRM_GOODS 买家已退货、等待卖家确认收货;SELLER_REFUSE_BUYER 卖家拒绝退款;SELLER_REFUSE_BUYER_RETURN_GOODS 卖家未收到货、拒绝退款;SELLER_RETURN_GOODS 商家确认收货并发送换货;CLOSED 售后退款关闭;SUCCESS 售后退款成功,不代表资金到账。未来非空字符串原样保留。"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"refunds": {
|
|
38
|
+
"type": "array", "items": { "$ref": "#/definitions/refund" },
|
|
39
|
+
"description": "本次父售后返回的资金条目,可明确为空;缺失、null、坏行或重复资金流水号均失败。无分页,不保证全部生命周期或账户交易全集。"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"preview": {
|
|
46
|
+
"type": "object", "required": ["requests"], "additionalProperties": false,
|
|
47
|
+
"properties": {
|
|
48
|
+
"requests": {
|
|
49
|
+
"type": "array", "minItems": 2, "maxItems": 2, "additionalItems": false,
|
|
50
|
+
"items": [
|
|
51
|
+
{
|
|
52
|
+
"type": "object", "required": ["method", "path", "body"], "additionalProperties": false,
|
|
53
|
+
"properties": {
|
|
54
|
+
"method": { "const": "POST" }, "path": { "const": "youzan.shop.basic.get/3.0.0" },
|
|
55
|
+
"body": { "type": "object", "additionalProperties": false, "maxProperties": 0 }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"type": "object", "required": ["method", "path", "body"], "additionalProperties": false,
|
|
60
|
+
"properties": {
|
|
61
|
+
"method": { "const": "POST" }, "path": { "const": "youzan.trade.refund.get/3.0.1" },
|
|
62
|
+
"body": {
|
|
63
|
+
"type": "object", "required": ["refund_id"], "additionalProperties": false,
|
|
64
|
+
"properties": { "refund_id": { "$ref": "#/definitions/identifier" } }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"error": {
|
|
73
|
+
"type": "object", "required": ["success", "code", "message"], "additionalProperties": false,
|
|
74
|
+
"properties": {
|
|
75
|
+
"success": { "const": false }, "code": { "const": -10001 },
|
|
76
|
+
"message": { "type": "string", "minLength": 1 },
|
|
77
|
+
"details": {
|
|
78
|
+
"type": "object", "required": ["category", "stage", "command", "targetScope", "requestState", "nextStep"],
|
|
79
|
+
"additionalProperties": false,
|
|
80
|
+
"properties": {
|
|
81
|
+
"category": { "enum": ["validation", "configuration", "authentication", "permission", "scope_unresolved", "http", "business_rejected", "not_found", "protocol", "transport", "limit"] },
|
|
82
|
+
"stage": { "enum": ["input", "context", "configuration", "shop-binding", "case", "refunds", "output-model", "transport", "response"] },
|
|
83
|
+
"command": { "const": "aftersale.refund.list" },
|
|
84
|
+
"targetScope": {
|
|
85
|
+
"type": "object", "additionalProperties": false,
|
|
86
|
+
"properties": { "kdtId": { "$ref": "#/definitions/shopId" }, "caseId": { "$ref": "#/definitions/identifier" } }
|
|
87
|
+
},
|
|
88
|
+
"requestState": { "enum": ["not_sent", "sent", "unknown"], "description": "not_sent 当前阶段请求未发送;sent 已发送并取得响应;unknown 无法确定请求结果。不自动重放。" },
|
|
89
|
+
"nextStep": { "type": "string", "minLength": 1 },
|
|
90
|
+
"api": { "type": "string", "minLength": 1 },
|
|
91
|
+
"httpStatus": { "type": "integer", "minimum": 100, "maximum": 599 },
|
|
92
|
+
"businessCode": { "$ref": "#/definitions/diagnosticCode" },
|
|
93
|
+
"gatewayCode": { "$ref": "#/definitions/diagnosticCode" },
|
|
94
|
+
"trace_id": { "$ref": "#/definitions/diagnosticId" },
|
|
95
|
+
"request_id": { "$ref": "#/definitions/diagnosticId" }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"definitions": {
|
|
101
|
+
"shopId": { "type": "integer", "minimum": 1, "maximum": 9007199254740991, "description": "显式目标店铺,须等于当前店铺上下文及凭证绑定店铺。" },
|
|
102
|
+
"identifier": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[^\\s\\u0000-\\u001f\\u007f-\\u009f]+$", "description": "原始标识字符串,无空白或控制字符,保留前导零。" },
|
|
103
|
+
"safeInteger": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 },
|
|
104
|
+
"refund": {
|
|
105
|
+
"type": "object", "required": ["refundNo", "amount", "status"], "additionalProperties": false,
|
|
106
|
+
"properties": {
|
|
107
|
+
"refundNo": { "$ref": "#/definitions/identifier", "description": "本次数组内不重复的资金流水号。" },
|
|
108
|
+
"refundId": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "资金元素退款 ID,保持原始字符串,不推断与父售后号或资金流水号等价。缺失省略,null 或错型失败。" },
|
|
109
|
+
"amount": { "type": "string", "pattern": "^(?:0|[1-9][0-9]*)\\.[0-9]{2}$", "description": "含邮费本条退款金额,默认 CNY,元,两位小数。精确范围 0.00..92233720368547758.07,不表示已到账,不跨条求和。" },
|
|
110
|
+
"status": { "$ref": "#/definitions/safeInteger", "description": "资金状态:0 退款中,三方失败结果可能延迟回写,长期为 0 也不能证明没有失败;1 退款失败;2 退款成功,不推断银行账户到账时间。未来未知安全整数保留。资金失败不等于查询失败。" },
|
|
111
|
+
"mode": { "$ref": "#/definitions/safeInteger", "description": "退款模式:0 原路退;1 现金退;2 标记退。3 语义未确定,只保留原码;未来未知安全整数同样保留,不推断动作。缺失省略,null 或错型失败。" },
|
|
112
|
+
"message": { "type": "string", "description": "资金处理说明,保留空串,不据此判断成功。缺失省略,null 或错型失败。" },
|
|
113
|
+
"payWayText": { "type": "string", "description": "资金支付渠道说明。缺失省略,空串保留,null 或错型失败。" },
|
|
114
|
+
"payWayLabel": { "type": "string", "description": "资金支付渠道描述标识。缺失省略,空串保留,null 或错型失败。" }
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"diagnosticCode": { "anyOf": [{ "$ref": "#/definitions/safeInteger" }, { "type": "string", "pattern": "^-?\\d{1,12}$" }] },
|
|
118
|
+
"diagnosticId": { "type": "string", "pattern": "^[a-zA-Z0-9_-]{1,128}$" }
|
|
119
|
+
},
|
|
120
|
+
"success": { "rule": "同店父售后身份核验通过,且确实取得完整、有效的本次资金数组后一次返回。" },
|
|
121
|
+
"errors": { "recovery": "核对原店铺、父售后号与授权;不自动重试、切店、换身份或办理退款。缺资金数组不能解释为没有资金记录。" },
|
|
122
|
+
"notes": [
|
|
123
|
+
"--kdt-id 省略时使用本次有效上下文 shopId;显式提供的非法值不回退。两者都缺失时拒绝;目标范围、同店核验和服务端权限仍按本命令校验。",
|
|
124
|
+
"执行仅限有效店铺上下文,显式店铺与当前 shopId 一致;总部拒绝。无上下文只允许显式目标本地预览,已给出的无效上下文始终失败。",
|
|
125
|
+
"预览不读取凭证、不联网,不证明权限或对象存在。实际先核验店铺,再读取父售后,最多两请求,不读取其他售后补齐。",
|
|
126
|
+
"仅查询本次父售后返回集合,无分页或到账承诺。不返回未选择的商品、协商、资金合计或不透明扩展。",
|
|
127
|
+
"累计原始响应上限 10MiB,最终 UTF-8 JSON 含换行上限 5MiB;超限取消、整体失败,不截断或输出部分成功。",
|
|
128
|
+
"应用 token 路径不证明员工 OAuth 或权限隔离通过。"
|
|
129
|
+
]
|
|
130
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { resolveTargetShop } = require('../../../../execution/target-shop');
|
|
4
|
+
const { createShopQuery, bindingPath, failure } = require('../../../../execution/shop-query');
|
|
5
|
+
|
|
6
|
+
const RESPONSE_LIMIT = 10 * 1024 * 1024;
|
|
7
|
+
const OUTPUT_LIMIT = 5 * 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
function reject(command, category, stage, scope = {}, requestState = 'not_sent', facts = {}) {
|
|
10
|
+
const error = failure(command, category, stage, scope, requestState, facts);
|
|
11
|
+
error.cliDetails.nextStep = stage === 'shop-binding' ?
|
|
12
|
+
'列表请求未发送。核对原凭证绑定店铺与显式目标,不自动重试、切店或换身份。' :
|
|
13
|
+
'停止并核对原店铺、筛选范围及服务端授权,不自动重试、切店或换身份;超限时不输出部分结果。';
|
|
14
|
+
return error;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function adaptError(error, command, scope) {
|
|
18
|
+
if (!error.cliDetails) return error;
|
|
19
|
+
const details = error.cliDetails;
|
|
20
|
+
const stage = ['scope', 'validation'].includes(details.stage) ? 'input' :
|
|
21
|
+
details.category === 'transport' ? 'transport' : details.stage;
|
|
22
|
+
return reject(command, details.category === 'parameter' ? 'validation' : details.category,
|
|
23
|
+
stage, scope, details.requestState, Object.fromEntries(
|
|
24
|
+
['api', 'httpStatus', 'businessCode', 'gatewayCode', 'trace_id', 'request_id']
|
|
25
|
+
.filter(key => Object.hasOwn(details, key)).map(key => [key, details[key]])));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function targetScope(options, context, command) {
|
|
29
|
+
if (!Object.hasOwn(options, 'kdtId')) throw reject(command, 'validation', 'input');
|
|
30
|
+
try { return resolveTargetShop(options, context, command); }
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw adaptError(error, command, error.cliDetails?.targetScope || {});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function createAftersaleQuery(scope, command, { responseLimit = RESPONSE_LIMIT } = {}) {
|
|
37
|
+
let responseBytes = 0;
|
|
38
|
+
try {
|
|
39
|
+
const read = await createShopQuery(scope, command, {
|
|
40
|
+
onResponse(response) { responseBytes += response.responseBytes; },
|
|
41
|
+
transportOptions: { maxResponseBytes: () => responseLimit - responseBytes }
|
|
42
|
+
});
|
|
43
|
+
return async (path, body, stage = 'cases') => {
|
|
44
|
+
try { return await read(path, body, stage, [200, '200']); }
|
|
45
|
+
catch (error) { throw adaptError(error, command, scope); }
|
|
46
|
+
};
|
|
47
|
+
} catch (error) { throw adaptError(error, command, scope); }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function serialize(output, format, command, scope, outputLimit = OUTPUT_LIMIT) {
|
|
51
|
+
const json = JSON.stringify(output, null, format === 'pretty' ? 2 : 0);
|
|
52
|
+
if (Buffer.byteLength(`${json}\n`, 'utf8') > outputLimit) {
|
|
53
|
+
throw reject(command, 'limit', 'output-model', scope, 'sent');
|
|
54
|
+
}
|
|
55
|
+
return json;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { bindingPath, reject, targetScope, createAftersaleQuery, serialize };
|