cbs-4px-merchant-cli 0.0.9 → 0.0.12
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.4px.md +5 -5
- package/README.cainiao.md +5 -5
- package/README.md +5 -5
- package/dist/cli.js +150 -32
- package/dist/cli.js.map +1 -1
- package/dist/commands/address.d.ts +7 -1
- package/dist/commands/address.js +29 -10
- package/dist/commands/address.js.map +1 -1
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +87 -0
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/config.js +120 -20
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/onboarding.d.ts +21 -7
- package/dist/commands/onboarding.js +95 -18
- package/dist/commands/onboarding.js.map +1 -1
- package/dist/commands/order.d.ts +38 -6
- package/dist/commands/order.js +251 -35
- package/dist/commands/order.js.map +1 -1
- package/dist/commands/platform.d.ts +50 -0
- package/dist/commands/platform.js +108 -6
- package/dist/commands/platform.js.map +1 -1
- package/dist/commands/product.d.ts +3 -0
- package/dist/commands/product.js +3 -0
- package/dist/commands/product.js.map +1 -1
- package/dist/commands/update.js +124 -3
- package/dist/commands/update.js.map +1 -1
- package/dist/errors.d.ts +29 -1
- package/dist/errors.js +11 -1
- package/dist/errors.js.map +1 -1
- package/dist/services/api-client.d.ts +49 -13
- package/dist/services/api-client.js +231 -45
- package/dist/services/api-client.js.map +1 -1
- package/dist/services/binary-source.d.ts +30 -0
- package/dist/services/binary-source.js +88 -0
- package/dist/services/binary-source.js.map +1 -0
- package/dist/services/binary-update.d.ts +62 -0
- package/dist/services/binary-update.js +179 -0
- package/dist/services/binary-update.js.map +1 -0
- package/dist/services/install-detector.d.ts +13 -1
- package/dist/services/install-detector.js +20 -1
- package/dist/services/install-detector.js.map +1 -1
- package/dist/types/index.d.ts +199 -12
- package/dist/utils/run-command.d.ts +6 -0
- package/dist/utils/run-command.js +13 -0
- package/dist/utils/run-command.js.map +1 -1
- package/package.json +7 -1
- package/skill/SKILL.md +14 -12
- package/skill/package.json +1 -1
- package/skill/reference/auth.md +20 -1
- package/skill/reference/index.md +3 -3
- package/skill/reference/onboarding.md +121 -52
- package/skill/reference/order.md +216 -36
- package/skill/reference/platform.md +85 -2
- package/skill/reference/product.md +3 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConfigManager } from './config-manager.js';
|
|
2
|
-
import type { GlobalOptions, TrackDetailDTO, LabelPrintDTO, OrderDetailResp, ProductRecommendDTO, ProductRecommendRequest, ClaimListResp, ClaimListRequest, OrderListRequest, OrderListResp, OrderAddressResp, CountrySearchResp, ProductSearchResp, TrackSubscriptionResp, ProductListResp, ProductDetailResp, FreightTrialResp, ProblemListRequest, ProblemListResp, AbnormalSuggestRequest, BatchAbnormalSuggestResponse, NotifyProblemListResp, ArrearOrderListResp, ServiceRepairOrderListRequest, RepairOrderListResp, ServiceOrderListRequest, ServiceOrderListResp, ServiceDetailResp, HandleRecordListResp, DssClaimDetailResp, ClaimReasonListResp, AccountBalanceResp, BillListResp, DailyFeeListResp, UnbilledDetailResp, UnbilledListResp, RechargeRecordListResp, TransferRecordListResp, FeeDetailResp, MaterialApplyListResp, MaterialDetailResp, MaterialQuotaResp, CollectAddressListResp, NoticeListResp, NoticeDetailResp, MessageListResp, MessageDetailResp, UnreadMessageCountResp, CargoKindListResp, CommodityListResp, HsCodeListResp, CustomerDeclareResp, FrozenRecordListResp, NotifyTypeListResp, SellerAccountListResp, PlatformOrderSyncResp, SyncOrderRecordListResp, PlatformOrderListResp, PlatformOrderDetailResp, IndivAuthRequest, CheckAuthCodeRequest, OrganAuthRequest, OpenBrandRequest, GATAuthRequest, OnboardingProgressResp, AuthScheduleResp, CustomerTypeItem, BrandAndAuthItem, ContractListResp, CreateOrderRequest, CreateOrderResp, EditOrderRequest, EditOrderResp, ForecastOrderResp, CancelOrderResp, InterceptOrderResp, AbnormalityHelpResp, ElectricProductResp, AIAbnormalSuggestResp } from '../types/index.js';
|
|
2
|
+
import type { GlobalOptions, TrackDetailDTO, LabelPrintDTO, OrderDetailResp, OrderFieldRulesResp, ProductRecommendDTO, ProductRecommendRequest, ClaimListResp, ClaimListRequest, OrderListRequest, OrderListResp, OrderAddressResp, AddressBookListRequest, CountrySearchResp, ProductSearchResp, TrackSubscriptionResp, ProductListResp, ProductDetailResp, FreightTrialResp, ProblemListRequest, ProblemListResp, AbnormalSuggestRequest, BatchAbnormalSuggestResponse, NotifyProblemListResp, ArrearOrderListResp, ServiceRepairOrderListRequest, RepairOrderListResp, ServiceOrderListRequest, ServiceOrderListResp, ServiceDetailResp, HandleRecordListResp, DssClaimDetailResp, ClaimReasonListResp, AccountBalanceResp, BillListResp, DailyFeeListResp, UnbilledDetailResp, UnbilledListResp, RechargeRecordListResp, TransferRecordListResp, FeeDetailResp, MaterialApplyListResp, MaterialDetailResp, MaterialQuotaResp, CollectAddressListResp, NoticeListResp, NoticeDetailResp, MessageListResp, MessageDetailResp, UnreadMessageCountResp, CargoKindListResp, CommodityListResp, HsCodeListResp, CustomerDeclareResp, FrozenRecordListResp, NotifyTypeListResp, SellerAccountListResp, PlatformOrderSyncResp, SyncOrderRecordListResp, PlatformOrderListResp, PlatformOrderDetailResp, IndivAuthRequest, CheckAuthCodeRequest, OrganAuthRequest, OpenBrandRequest, GATAuthRequest, OnboardingProgressResp, AuthScheduleResp, CustomerTypeItem, BrandAndAuthItem, ContractListResp, CreateOrderRequest, CreateOrderResp, EditOrderRequest, EditOrderResp, ForecastOrderResp, CancelOrderResp, InterceptOrderResp, AbnormalityHelpResp, ElectricProductResp, AIAbnormalSuggestResp, PlatformOrderUpdateResp, PlatformOrderPushFailure } from '../types/index.js';
|
|
3
3
|
export declare class ApiClient {
|
|
4
4
|
private readonly config;
|
|
5
5
|
private readonly verbose;
|
|
@@ -10,14 +10,23 @@ export declare class ApiClient {
|
|
|
10
10
|
/** 返回 N 天前的日期 yyyy-MM-dd,与 bportal MCP Server 的 defaultStartDate 对齐 */
|
|
11
11
|
private recentDate;
|
|
12
12
|
private createHttp;
|
|
13
|
+
/**
|
|
14
|
+
* Uses the AgentZ access token only to mint the merchant API Key. The token
|
|
15
|
+
* must never be persisted, returned, or printed because it belongs to the
|
|
16
|
+
* host Agent client rather than this CLI.
|
|
17
|
+
*/
|
|
18
|
+
generateAgentzApiKey(accessToken: string): Promise<string>;
|
|
13
19
|
private get;
|
|
14
20
|
private post;
|
|
15
21
|
/**
|
|
16
22
|
* 发送 JSON body 的 POST。
|
|
17
23
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
24
|
+
* 用于后端以 `@Body` 绑定的接口(order create/edit、problem order list、
|
|
25
|
+
* custody 全部、onboarding 的实名与开通品牌接口)。后端的 `@Body` 解析器直接
|
|
26
|
+
* 对原始 body 做 `JSON.parseObject`,收到表单串会抛
|
|
27
|
+
* `Failed to parse JSON body as ...`,因此这类接口不能走 `post()`。
|
|
28
|
+
* 其余接口必须继续调用 `post()`,因为 Solon 的 `@Param` 从表单取值,
|
|
29
|
+
* 发送 JSON body 会返回 400。
|
|
21
30
|
*/
|
|
22
31
|
private postJson;
|
|
23
32
|
/** 生成 JSON body 请求的 dry-run 预览,不发送网络请求。 */
|
|
@@ -31,16 +40,27 @@ export declare class ApiClient {
|
|
|
31
40
|
listTrackSubscriptionByProduct(productCode?: string, countryCode?: string, pageNum?: number, pageSize?: number): Promise<TrackSubscriptionResp>;
|
|
32
41
|
getLabelPrint(orderNo: string): Promise<LabelPrintDTO>;
|
|
33
42
|
getOrderDetail(orderNo: string): Promise<OrderDetailResp>;
|
|
43
|
+
/**
|
|
44
|
+
* 查询下单字段规则(必填性的权威来源)。
|
|
45
|
+
*
|
|
46
|
+
* 必填由规则库按 (产品, 目的国, 客户) 配置,同一字段在不同产品下结论不同,
|
|
47
|
+
* 因此建单前必须查询而不能沿用本地清单。
|
|
48
|
+
*/
|
|
49
|
+
getOrderFieldRules(productCode: string, country: string, requiredOnly?: boolean): Promise<OrderFieldRulesResp>;
|
|
34
50
|
listOrders(request: OrderListRequest): Promise<OrderListResp>;
|
|
35
|
-
listAddressBook(): Promise<OrderAddressResp>;
|
|
51
|
+
listAddressBook(request: AddressBookListRequest): Promise<OrderAddressResp>;
|
|
36
52
|
searchDestinationCountry(key: string, productCode?: string): Promise<CountrySearchResp>;
|
|
37
53
|
/** JSON body,`status: 'D'` 表示保存草稿而不预报。 */
|
|
38
54
|
createOrder(request: CreateOrderRequest): Promise<CreateOrderResp>;
|
|
39
55
|
/** JSON body;仅草稿和已取消订单可编辑。 */
|
|
40
56
|
editOrder(request: EditOrderRequest): Promise<EditOrderResp>;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
/** 草稿转预报。传客户订单号,后端按单号反查主键。 */
|
|
58
|
+
forecastOrder(orderNos: string): Promise<ForecastOrderResp>;
|
|
59
|
+
/** 取消订单。传单号,后端按单号反查主键。 */
|
|
60
|
+
cancelOrder(orderNos: string, cancelReason: string): Promise<CancelOrderResp>;
|
|
61
|
+
/** 申请拦截。传单号,后端按单号反查主键。 */
|
|
62
|
+
interceptOrder(orderNos: string): Promise<InterceptOrderResp>;
|
|
63
|
+
uninterceptOrder(orderNos: string): Promise<InterceptOrderResp>;
|
|
44
64
|
searchProduct(key: string, countryCode?: string): Promise<ProductSearchResp>;
|
|
45
65
|
getProductRecommend(request: ProductRecommendRequest): Promise<ProductRecommendDTO>;
|
|
46
66
|
listAvailableProducts(country: string, startCity?: string): Promise<ProductListResp>;
|
|
@@ -106,21 +126,37 @@ export declare class ApiClient {
|
|
|
106
126
|
listSellerAccounts(pageNum?: number, pageSize?: number): Promise<SellerAccountListResp>;
|
|
107
127
|
syncPlatformOrders(sellerAccountId: string, timeFrom: string, timeTo: string): Promise<PlatformOrderSyncResp>;
|
|
108
128
|
listPlatformSyncRecords(pageNum?: number, pageSize?: number): Promise<SyncOrderRecordListResp>;
|
|
109
|
-
listPlatformOrders(status?: number, keyword?: string, pageNum?: number, pageSize?: number): Promise<PlatformOrderListResp>;
|
|
110
|
-
|
|
129
|
+
listPlatformOrders(status?: number, keyword?: string, filters?: Record<string, string | undefined>, pageNum?: number, pageSize?: number): Promise<PlatformOrderListResp>;
|
|
130
|
+
updatePlatformOrders(ids: string[], fields?: Record<string, string | undefined>): Promise<PlatformOrderUpdateResp>;
|
|
131
|
+
/**
|
|
132
|
+
* 推送平台订单。返回的是**失败明细数组**,空数组即全部成功。
|
|
133
|
+
* 已推送的单会被后端按状态拒绝并出现在返回里(防重复建物流单)。
|
|
134
|
+
*/
|
|
135
|
+
pushPlatformOrders(ids: string): Promise<PlatformOrderPushFailure[] | null>;
|
|
111
136
|
getPlatformOrderDetail(id: string): Promise<PlatformOrderDetailResp>;
|
|
112
137
|
/** Returns the flowId that `checkAuthCode` needs. */
|
|
113
138
|
startIndivRealNameAuth(request: IndivAuthRequest): Promise<string>;
|
|
114
139
|
checkAuthCode(request: CheckAuthCodeRequest): Promise<string>;
|
|
140
|
+
/**
|
|
141
|
+
* 企业实名认证:服务端把企业证件、法人、认证方式与完整注册地址一并校验,
|
|
142
|
+
* 因此这里整体组装后再发出,缺字段时由命令层提前拦截而不是靠服务端报错。
|
|
143
|
+
*/
|
|
115
144
|
startOrganRealNameAuth(request: OrganAuthRequest): Promise<string>;
|
|
116
145
|
openBrandAndApplyContract(request: OpenBrandRequest): Promise<string>;
|
|
146
|
+
/**
|
|
147
|
+
* 重复入驻场景下通知当前客户对应的销售(钉钉卡片)。
|
|
148
|
+
* 客户编码由服务端从 API Key 上下文取,通知内容服务端写死,本接口无入参、无返回数据。
|
|
149
|
+
*/
|
|
150
|
+
notifyRepeatEnterSaler(): Promise<void>;
|
|
117
151
|
authSaveGAT(request: GATAuthRequest): Promise<string>;
|
|
118
|
-
/** amount is in cents — the backend rejects yuan. */
|
|
119
|
-
checkRemitAmount(flowId: string, amount: number): Promise<string>;
|
|
120
152
|
queryOnboardingProgress(): Promise<OnboardingProgressResp>;
|
|
121
153
|
queryAuthSchedule(): Promise<AuthScheduleResp>;
|
|
122
154
|
queryCustomerType(): Promise<CustomerTypeItem[]>;
|
|
123
155
|
queryBrandAndAuth(customerType?: string): Promise<BrandAndAuthItem[]>;
|
|
124
156
|
getContractList(pageNum?: number, pageSize?: number): Promise<ContractListResp>;
|
|
125
|
-
|
|
157
|
+
/**
|
|
158
|
+
* `status` 必填,取自 `contract list`:服务端会重新查合同详情并以实际状态为准,
|
|
159
|
+
* 详情未返回状态时用它兜底,所以状态码只能来自列表,不能猜。
|
|
160
|
+
*/
|
|
161
|
+
getContractSignUrl(contractCode: string, status: number): Promise<string>;
|
|
126
162
|
}
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { ApiError, DryRunSignal } from '../errors.js';
|
|
2
|
+
import { ApiError, BportalError, DryRunSignal } from '../errors.js';
|
|
3
3
|
const API_PREFIX = '/api/merchant/tools';
|
|
4
|
+
const AGENTZ_API_KEY_GENERATE_PATH = '/api/agentz/apikey/generate';
|
|
5
|
+
/**
|
|
6
|
+
* 后端以 `@Body` 绑定(整体 JSON 反序列化)的端点,必须走 {@link ApiClient.postJson}。
|
|
7
|
+
*
|
|
8
|
+
* 这类端点收到表单串会在 `JSON.parseObject` 处抛异常,后端统一兜成
|
|
9
|
+
* `SYSTEM_ERROR / 系统繁忙,请稍后再试`——症状是「不传参侥幸能通、一传参就报系统繁忙」,
|
|
10
|
+
* 极难从错误信息反推。故在 `post()` 里对照本清单做开发期断言,写错立即报错而不是发出去。
|
|
11
|
+
*
|
|
12
|
+
* 新增工具时:后端方法签名是 `@Body XxxRequest` 就登记到这里并用 `postJson`;
|
|
13
|
+
* 是 `@Param` 就用 `post`。路径为 API_PREFIX 之后的部分。
|
|
14
|
+
*/
|
|
15
|
+
const JSON_BODY_PATHS = new Set([
|
|
16
|
+
'/order/list',
|
|
17
|
+
'/order/create',
|
|
18
|
+
'/order/edit',
|
|
19
|
+
'/order/field/rules',
|
|
20
|
+
'/product-recommendation',
|
|
21
|
+
'/shipping-fee/calculate',
|
|
22
|
+
'/problem/list',
|
|
23
|
+
'/problem/suggestions',
|
|
24
|
+
'/problem/intercepted/list',
|
|
25
|
+
'/service/claim/order/list',
|
|
26
|
+
'/service-order/aftersale/list',
|
|
27
|
+
'/service-order/check/list',
|
|
28
|
+
'/service-order/complaint/list',
|
|
29
|
+
'/finance/fee/daily/list',
|
|
30
|
+
'/finance/recharge/list',
|
|
31
|
+
'/finance/recharge/offline/list',
|
|
32
|
+
]);
|
|
4
33
|
/**
|
|
5
34
|
* Params whose values must never be echoed back to the terminal.
|
|
6
35
|
*
|
|
@@ -10,13 +39,21 @@ const API_PREFIX = '/api/merchant/tools';
|
|
|
10
39
|
* transcripts. Masking applies to the printed copy only — the request itself
|
|
11
40
|
* still carries the real value.
|
|
12
41
|
*/
|
|
13
|
-
const SENSITIVE_PARAMS = new Set(['certNumber', 'bankCard', 'authCode', 'mobile']);
|
|
42
|
+
const SENSITIVE_PARAMS = new Set(['certNumber', 'legalCertNumber', 'bankCard', 'authCode', 'mobile']);
|
|
14
43
|
/**
|
|
15
44
|
* 嵌套 JSON body 同样需要递归遮蔽敏感字段,否则订单的
|
|
16
45
|
* `receiver.mobile`、`sender.phone` 会被 `--dry-run` / `--verbose`
|
|
17
46
|
* 原样写入终端回滚区和平台日志。
|
|
18
47
|
*/
|
|
19
|
-
const SENSITIVE_BODY_KEYS = new Set([
|
|
48
|
+
const SENSITIVE_BODY_KEYS = new Set([
|
|
49
|
+
'certNumber',
|
|
50
|
+
// 法人身份证号与企业证件号同级敏感,遗漏会让企业实名的 --dry-run 明文回显
|
|
51
|
+
'legalCertNumber',
|
|
52
|
+
'bankCard',
|
|
53
|
+
'authCode',
|
|
54
|
+
'mobile',
|
|
55
|
+
'phone',
|
|
56
|
+
]);
|
|
20
57
|
function maskSensitive(params) {
|
|
21
58
|
const out = {};
|
|
22
59
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -44,6 +81,26 @@ function toForm(params) {
|
|
|
44
81
|
}
|
|
45
82
|
return form;
|
|
46
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* 企业证件类型的数字值 → 枚举名,与后端 `OrganCertTypeEnum` 一一对应。
|
|
86
|
+
* 企业实名同时传 certType 与 certTypeEnum,两者不一致时以哪个为准并不确定,
|
|
87
|
+
* 所以 CLI 只收数字,枚举名统一从这里推导。
|
|
88
|
+
*/
|
|
89
|
+
const ORGAN_CERT_TYPE_ENUMS = {
|
|
90
|
+
0: 'CRED_ORG_USCC',
|
|
91
|
+
1: 'CRED_ORG_CODE',
|
|
92
|
+
2: 'CRED_ORG_REGCODE',
|
|
93
|
+
3: 'CRED_ORG_BUSINESS_REGISTTATION_CODE',
|
|
94
|
+
4: 'CRED_ORG_TAX_REGISTTATION_CODE',
|
|
95
|
+
5: 'CRED_ORG_LEGAL_PERSON_CODE',
|
|
96
|
+
6: 'CRED_ORG_ENT_LEGAL_PERSON_CODE',
|
|
97
|
+
7: 'CRED_ORG_SOCIAL_REG_CODE',
|
|
98
|
+
8: 'CRED_ORG_PRIVATE_NON_ENT_REG_CODE',
|
|
99
|
+
9: 'CRED_ORG_FOREIGN_ENT_REG_CODE',
|
|
100
|
+
10: 'CRED_ORG_GOV_APPROVAL',
|
|
101
|
+
11: 'CODE_ORG_CUSTOM',
|
|
102
|
+
12: 'CRED_ORG_UNKNOWN',
|
|
103
|
+
};
|
|
47
104
|
export class ApiClient {
|
|
48
105
|
config;
|
|
49
106
|
verbose;
|
|
@@ -75,6 +132,37 @@ export class ApiClient {
|
|
|
75
132
|
}
|
|
76
133
|
return axios.create({ baseURL, headers, timeout: 15000 });
|
|
77
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Uses the AgentZ access token only to mint the merchant API Key. The token
|
|
137
|
+
* must never be persisted, returned, or printed because it belongs to the
|
|
138
|
+
* host Agent client rather than this CLI.
|
|
139
|
+
*/
|
|
140
|
+
async generateAgentzApiKey(accessToken) {
|
|
141
|
+
const baseURL = await this.config.resolveBaseUrl();
|
|
142
|
+
const http = axios.create({
|
|
143
|
+
baseURL,
|
|
144
|
+
headers: {
|
|
145
|
+
'Content-Type': 'application/json',
|
|
146
|
+
'X-Source': 'cli',
|
|
147
|
+
Authorization: `Bearer ${accessToken}`,
|
|
148
|
+
},
|
|
149
|
+
timeout: 15000,
|
|
150
|
+
});
|
|
151
|
+
if (this.verbose) {
|
|
152
|
+
process.stderr.write(`> POST ${baseURL}${AGENTZ_API_KEY_GENERATE_PATH}\n`);
|
|
153
|
+
process.stderr.write('> Authorization: Bearer ****\n');
|
|
154
|
+
}
|
|
155
|
+
const res = await http.post(AGENTZ_API_KEY_GENERATE_PATH);
|
|
156
|
+
if (this.verbose) {
|
|
157
|
+
process.stderr.write(`< ${res.status} ${res.statusText}\n`);
|
|
158
|
+
}
|
|
159
|
+
const response = res.data;
|
|
160
|
+
const apiKey = response.data?.apiKey;
|
|
161
|
+
if (response.result !== 0 || !apiKey) {
|
|
162
|
+
throw new ApiError(response.code ?? 'AGENTZ_API_KEY_GENERATION_FAILED', response.message ?? 'AgentZ API Key 生成失败', '请确认 AgentZ 登录态有效后重试');
|
|
163
|
+
}
|
|
164
|
+
return apiKey;
|
|
165
|
+
}
|
|
78
166
|
async get(path, params, withAuth) {
|
|
79
167
|
if (this.dryRun) {
|
|
80
168
|
const info = await this.dryRunInfo(path, params, withAuth);
|
|
@@ -96,11 +184,14 @@ export class ApiClient {
|
|
|
96
184
|
}
|
|
97
185
|
const resp = res.data;
|
|
98
186
|
if (!resp.success) {
|
|
99
|
-
throw new ApiError(resp.errCode, resp.errMsg, '请检查单号是否正确、API Key 是否有对应权限,或稍后重试');
|
|
187
|
+
throw new ApiError(resp.errCode, resp.errMsg, '请检查单号是否正确、API Key 是否有对应权限,或稍后重试', resp.data);
|
|
100
188
|
}
|
|
101
189
|
return resp.data;
|
|
102
190
|
}
|
|
103
191
|
async post(path, params, withAuth) {
|
|
192
|
+
if (JSON_BODY_PATHS.has(path.slice(API_PREFIX.length))) {
|
|
193
|
+
throw new BportalError(`${path} 在后端以 @Body 绑定,必须调用 postJson();用 post() 发表单会被兜成 SYSTEM_ERROR`, 'CLIENT_BUG', '这是 CLI 代码缺陷,请把该调用改为 postJson()');
|
|
194
|
+
}
|
|
104
195
|
if (this.dryRun) {
|
|
105
196
|
const info = await this.dryRunPostInfo(path, params, withAuth);
|
|
106
197
|
console.log(JSON.stringify(info, null, 2));
|
|
@@ -125,16 +216,19 @@ export class ApiClient {
|
|
|
125
216
|
}
|
|
126
217
|
const resp = res.data;
|
|
127
218
|
if (!resp.success) {
|
|
128
|
-
throw new ApiError(resp.errCode, resp.errMsg, '请检查参数是否正确、API Key 是否有对应权限,或稍后重试');
|
|
219
|
+
throw new ApiError(resp.errCode, resp.errMsg, '请检查参数是否正确、API Key 是否有对应权限,或稍后重试', resp.data);
|
|
129
220
|
}
|
|
130
221
|
return resp.data;
|
|
131
222
|
}
|
|
132
223
|
/**
|
|
133
224
|
* 发送 JSON body 的 POST。
|
|
134
225
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
226
|
+
* 用于后端以 `@Body` 绑定的接口(order create/edit、problem order list、
|
|
227
|
+
* custody 全部、onboarding 的实名与开通品牌接口)。后端的 `@Body` 解析器直接
|
|
228
|
+
* 对原始 body 做 `JSON.parseObject`,收到表单串会抛
|
|
229
|
+
* `Failed to parse JSON body as ...`,因此这类接口不能走 `post()`。
|
|
230
|
+
* 其余接口必须继续调用 `post()`,因为 Solon 的 `@Param` 从表单取值,
|
|
231
|
+
* 发送 JSON body 会返回 400。
|
|
138
232
|
*/
|
|
139
233
|
async postJson(path, body, withAuth) {
|
|
140
234
|
if (this.dryRun) {
|
|
@@ -159,7 +253,7 @@ export class ApiClient {
|
|
|
159
253
|
}
|
|
160
254
|
const resp = res.data;
|
|
161
255
|
if (!resp.success) {
|
|
162
|
-
throw new ApiError(resp.errCode, resp.errMsg, '请检查订单字段是否完整、产品与目的国是否匹配,或稍后重试');
|
|
256
|
+
throw new ApiError(resp.errCode, resp.errMsg, '请检查订单字段是否完整、产品与目的国是否匹配,或稍后重试', resp.data);
|
|
163
257
|
}
|
|
164
258
|
return resp.data;
|
|
165
259
|
}
|
|
@@ -226,6 +320,18 @@ export class ApiClient {
|
|
|
226
320
|
async getOrderDetail(orderNo) {
|
|
227
321
|
return this.get(`${API_PREFIX}/order/detail`, { orderNo }, true);
|
|
228
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* 查询下单字段规则(必填性的权威来源)。
|
|
325
|
+
*
|
|
326
|
+
* 必填由规则库按 (产品, 目的国, 客户) 配置,同一字段在不同产品下结论不同,
|
|
327
|
+
* 因此建单前必须查询而不能沿用本地清单。
|
|
328
|
+
*/
|
|
329
|
+
async getOrderFieldRules(productCode, country, requiredOnly) {
|
|
330
|
+
const body = { productCode, country };
|
|
331
|
+
if (requiredOnly)
|
|
332
|
+
body.requiredOnly = true;
|
|
333
|
+
return this.postJson(`${API_PREFIX}/order/field/rules`, body, true);
|
|
334
|
+
}
|
|
229
335
|
async listOrders(request) {
|
|
230
336
|
const body = {};
|
|
231
337
|
if (request.statusName)
|
|
@@ -234,6 +340,8 @@ export class ApiClient {
|
|
|
234
340
|
body.customerOrderNo = request.customerOrderNo;
|
|
235
341
|
if (request.fpxTrackNo)
|
|
236
342
|
body.fpxTrackNo = request.fpxTrackNo;
|
|
343
|
+
if (request.queryDateType)
|
|
344
|
+
body.queryDateType = request.queryDateType;
|
|
237
345
|
if (request.queryStartDate)
|
|
238
346
|
body.queryStartDate = request.queryStartDate;
|
|
239
347
|
if (request.queryEndDate)
|
|
@@ -242,14 +350,37 @@ export class ApiClient {
|
|
|
242
350
|
body.productCode = request.productCode;
|
|
243
351
|
if (request.destinationCountry)
|
|
244
352
|
body.destinationCountry = request.destinationCountry;
|
|
353
|
+
if (request.consigneeName)
|
|
354
|
+
body.consigneeName = request.consigneeName;
|
|
355
|
+
if (request.buyerId)
|
|
356
|
+
body.buyerId = request.buyerId;
|
|
357
|
+
if (request.isPrint)
|
|
358
|
+
body.isPrint = request.isPrint;
|
|
359
|
+
if (request.odaConfirm)
|
|
360
|
+
body.odaConfirm = request.odaConfirm;
|
|
361
|
+
if (request.interceptStatus)
|
|
362
|
+
body.interceptStatus = request.interceptStatus;
|
|
363
|
+
if (request.changeNoStatus)
|
|
364
|
+
body.changeNoStatus = request.changeNoStatus;
|
|
365
|
+
if (request.isHistory)
|
|
366
|
+
body.isHistory = request.isHistory;
|
|
245
367
|
if (request.pageNum != null)
|
|
246
368
|
body.pageNum = request.pageNum;
|
|
247
369
|
if (request.pageSize != null)
|
|
248
370
|
body.pageSize = request.pageSize;
|
|
249
|
-
return this.
|
|
371
|
+
return this.postJson(`${API_PREFIX}/order/list`, body, true);
|
|
250
372
|
}
|
|
251
|
-
async listAddressBook() {
|
|
252
|
-
|
|
373
|
+
async listAddressBook(request) {
|
|
374
|
+
const body = {};
|
|
375
|
+
if (request.addressType)
|
|
376
|
+
body.addressType = request.addressType;
|
|
377
|
+
if (request.keyword)
|
|
378
|
+
body.keyword = request.keyword;
|
|
379
|
+
if (request.pageNum != null)
|
|
380
|
+
body.pageNum = request.pageNum;
|
|
381
|
+
if (request.pageSize != null)
|
|
382
|
+
body.pageSize = request.pageSize;
|
|
383
|
+
return this.post(`${API_PREFIX}/address-book/list`, body, true);
|
|
253
384
|
}
|
|
254
385
|
async searchDestinationCountry(key, productCode) {
|
|
255
386
|
const body = { key };
|
|
@@ -266,14 +397,20 @@ export class ApiClient {
|
|
|
266
397
|
async editOrder(request) {
|
|
267
398
|
return this.postJson(`${API_PREFIX}/order/edit`, request, true);
|
|
268
399
|
}
|
|
269
|
-
|
|
270
|
-
|
|
400
|
+
/** 草稿转预报。传客户订单号,后端按单号反查主键。 */
|
|
401
|
+
async forecastOrder(orderNos) {
|
|
402
|
+
return this.post(`${API_PREFIX}/order/forecast`, { orderNos }, true);
|
|
403
|
+
}
|
|
404
|
+
/** 取消订单。传单号,后端按单号反查主键。 */
|
|
405
|
+
async cancelOrder(orderNos, cancelReason) {
|
|
406
|
+
return this.post(`${API_PREFIX}/order/cancel`, { orderNos, cancelReason }, true);
|
|
271
407
|
}
|
|
272
|
-
|
|
273
|
-
|
|
408
|
+
/** 申请拦截。传单号,后端按单号反查主键。 */
|
|
409
|
+
async interceptOrder(orderNos) {
|
|
410
|
+
return this.post(`${API_PREFIX}/order/intercept`, { orderNos }, true);
|
|
274
411
|
}
|
|
275
|
-
async
|
|
276
|
-
return this.post(`${API_PREFIX}/order/
|
|
412
|
+
async uninterceptOrder(orderNos) {
|
|
413
|
+
return this.post(`${API_PREFIX}/order/unintercept`, { orderNos }, true);
|
|
277
414
|
}
|
|
278
415
|
async searchProduct(key, countryCode) {
|
|
279
416
|
const body = { key };
|
|
@@ -294,7 +431,7 @@ export class ApiClient {
|
|
|
294
431
|
body.isBattery = request.isBattery;
|
|
295
432
|
if (request.declareValue != null)
|
|
296
433
|
body.declareValue = request.declareValue;
|
|
297
|
-
return this.
|
|
434
|
+
return this.postJson(`${API_PREFIX}/product-recommendation`, body, true);
|
|
298
435
|
}
|
|
299
436
|
async listAvailableProducts(country, startCity) {
|
|
300
437
|
const body = { country };
|
|
@@ -322,7 +459,7 @@ export class ApiClient {
|
|
|
322
459
|
body.isBattery = request.isBattery;
|
|
323
460
|
if (request.declareValue != null)
|
|
324
461
|
body.declareValue = request.declareValue;
|
|
325
|
-
return this.
|
|
462
|
+
return this.postJson(`${API_PREFIX}/shipping-fee/calculate`, body, true);
|
|
326
463
|
}
|
|
327
464
|
async getEstimatedDeliveryTime(countryCode, productCode) {
|
|
328
465
|
return this.get(`${API_PREFIX}/estimated-delivery-time`, { countryCode, productCode }, true);
|
|
@@ -342,7 +479,7 @@ export class ApiClient {
|
|
|
342
479
|
body.pageNum = request.pageNum;
|
|
343
480
|
if (request.pageSize != null)
|
|
344
481
|
body.pageSize = request.pageSize;
|
|
345
|
-
return this.
|
|
482
|
+
return this.postJson(`${API_PREFIX}/service/claim/order/list`, body, true);
|
|
346
483
|
}
|
|
347
484
|
// ==================== Problem / Abnormal ====================
|
|
348
485
|
async listProblemOrder(request) {
|
|
@@ -368,7 +505,7 @@ export class ApiClient {
|
|
|
368
505
|
body.items = JSON.stringify(request.items);
|
|
369
506
|
if (request.orderNoItems)
|
|
370
507
|
body.orderNoItems = request.orderNoItems.join(',');
|
|
371
|
-
return this.
|
|
508
|
+
return this.postJson(`${API_PREFIX}/problem/suggestions`, body, true);
|
|
372
509
|
}
|
|
373
510
|
async listProblemNotify(pageNum, pageSize) {
|
|
374
511
|
const body = {};
|
|
@@ -402,6 +539,8 @@ export class ApiClient {
|
|
|
402
539
|
body.customerOrderNo = request.customerOrderNo;
|
|
403
540
|
if (request.fpxTrackNo)
|
|
404
541
|
body.fpxTrackNo = request.fpxTrackNo;
|
|
542
|
+
if (request.queryDateType)
|
|
543
|
+
body.queryDateType = request.queryDateType;
|
|
405
544
|
if (request.queryStartDate)
|
|
406
545
|
body.queryStartDate = request.queryStartDate;
|
|
407
546
|
if (request.queryEndDate)
|
|
@@ -410,7 +549,7 @@ export class ApiClient {
|
|
|
410
549
|
body.pageNum = request.pageNum;
|
|
411
550
|
if (request.pageSize != null)
|
|
412
551
|
body.pageSize = request.pageSize;
|
|
413
|
-
return this.
|
|
552
|
+
return this.postJson(`${API_PREFIX}/problem/intercepted/list`, body, true);
|
|
414
553
|
}
|
|
415
554
|
// ==================== Service Center ====================
|
|
416
555
|
async listServiceOrderAftersale(request) {
|
|
@@ -431,7 +570,7 @@ export class ApiClient {
|
|
|
431
570
|
body.pageNum = request.pageNum;
|
|
432
571
|
if (request.pageSize != null)
|
|
433
572
|
body.pageSize = request.pageSize;
|
|
434
|
-
return this.
|
|
573
|
+
return this.postJson(`${API_PREFIX}/service-order/aftersale/list`, body, true);
|
|
435
574
|
}
|
|
436
575
|
async listServiceOrderCheck(request) {
|
|
437
576
|
const body = {};
|
|
@@ -449,7 +588,7 @@ export class ApiClient {
|
|
|
449
588
|
body.pageNum = request.pageNum;
|
|
450
589
|
if (request.pageSize != null)
|
|
451
590
|
body.pageSize = request.pageSize;
|
|
452
|
-
return this.
|
|
591
|
+
return this.postJson(`${API_PREFIX}/service-order/check/list`, body, true);
|
|
453
592
|
}
|
|
454
593
|
async listServiceOrderComplaint(request) {
|
|
455
594
|
const body = {};
|
|
@@ -467,7 +606,7 @@ export class ApiClient {
|
|
|
467
606
|
body.pageNum = request.pageNum;
|
|
468
607
|
if (request.pageSize != null)
|
|
469
608
|
body.pageSize = request.pageSize;
|
|
470
|
-
return this.
|
|
609
|
+
return this.postJson(`${API_PREFIX}/service-order/complaint/list`, body, true);
|
|
471
610
|
}
|
|
472
611
|
async getServiceOrderCheckDetail(serviceNo) {
|
|
473
612
|
const body = { serviceNo };
|
|
@@ -521,7 +660,7 @@ export class ApiClient {
|
|
|
521
660
|
body.pageNum = pageNum;
|
|
522
661
|
if (pageSize != null)
|
|
523
662
|
body.pageSize = pageSize;
|
|
524
|
-
return this.
|
|
663
|
+
return this.postJson(`${API_PREFIX}/finance/fee/daily/list`, body, true);
|
|
525
664
|
}
|
|
526
665
|
async getUnbilledFeeDetail(businessNo, currencyCode) {
|
|
527
666
|
const body = { businessNo, currencyCode };
|
|
@@ -551,7 +690,7 @@ export class ApiClient {
|
|
|
551
690
|
body.pageNum = pageNum;
|
|
552
691
|
if (pageSize != null)
|
|
553
692
|
body.pageSize = pageSize;
|
|
554
|
-
return this.
|
|
693
|
+
return this.postJson(`${API_PREFIX}/finance/recharge/list`, body, true);
|
|
555
694
|
}
|
|
556
695
|
async listOfflineRechargeRecord(businessSystem, currency, tradeNo, status, startDate, endDate, pageNum, pageSize) {
|
|
557
696
|
const body = {};
|
|
@@ -571,7 +710,7 @@ export class ApiClient {
|
|
|
571
710
|
body.pageNum = pageNum;
|
|
572
711
|
if (pageSize != null)
|
|
573
712
|
body.pageSize = pageSize;
|
|
574
|
-
return this.
|
|
713
|
+
return this.postJson(`${API_PREFIX}/finance/recharge/offline/list`, body, true);
|
|
575
714
|
}
|
|
576
715
|
async getBilledFeeDetail(businessNo) {
|
|
577
716
|
const body = { businessNo };
|
|
@@ -753,18 +892,38 @@ export class ApiClient {
|
|
|
753
892
|
body.pageSize = pageSize;
|
|
754
893
|
return this.post(`${API_PREFIX}/platformOrder/syncRecord/list`, body, true);
|
|
755
894
|
}
|
|
756
|
-
async listPlatformOrders(status, keyword, pageNum, pageSize) {
|
|
895
|
+
async listPlatformOrders(status, keyword, filters, pageNum, pageSize) {
|
|
757
896
|
const body = {};
|
|
758
897
|
if (status != null)
|
|
759
898
|
body.status = status;
|
|
760
899
|
if (keyword)
|
|
761
900
|
body.keyword = keyword;
|
|
901
|
+
if (filters) {
|
|
902
|
+
for (const [k, v] of Object.entries(filters)) {
|
|
903
|
+
if (v)
|
|
904
|
+
body[k] = v;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
762
907
|
if (pageNum != null)
|
|
763
908
|
body.pageNum = pageNum;
|
|
764
909
|
if (pageSize != null)
|
|
765
910
|
body.pageSize = pageSize;
|
|
766
911
|
return this.post(`${API_PREFIX}/platformOrder/order/list`, body, true);
|
|
767
912
|
}
|
|
913
|
+
async updatePlatformOrders(ids, fields) {
|
|
914
|
+
const body = { ids };
|
|
915
|
+
if (fields) {
|
|
916
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
917
|
+
if (v)
|
|
918
|
+
body[k] = v;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return this.post(`${API_PREFIX}/platformOrder/order/update`, body, true);
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* 推送平台订单。返回的是**失败明细数组**,空数组即全部成功。
|
|
925
|
+
* 已推送的单会被后端按状态拒绝并出现在返回里(防重复建物流单)。
|
|
926
|
+
*/
|
|
768
927
|
async pushPlatformOrders(ids) {
|
|
769
928
|
return this.post(`${API_PREFIX}/platformOrder/order/push`, { ids }, true);
|
|
770
929
|
}
|
|
@@ -784,7 +943,7 @@ export class ApiClient {
|
|
|
784
943
|
body.bankCard = request.bankCard;
|
|
785
944
|
if (request.isAgent != null)
|
|
786
945
|
body.isAgent = request.isAgent;
|
|
787
|
-
return this.
|
|
946
|
+
return this.postJson(`${API_PREFIX}/onboarding/indiv-auth`, body, true);
|
|
788
947
|
}
|
|
789
948
|
async checkAuthCode(request) {
|
|
790
949
|
const body = {
|
|
@@ -795,18 +954,38 @@ export class ApiClient {
|
|
|
795
954
|
body.authType = request.authType;
|
|
796
955
|
if (request.isAgent != null)
|
|
797
956
|
body.isAgent = request.isAgent;
|
|
798
|
-
return this.
|
|
957
|
+
return this.postJson(`${API_PREFIX}/onboarding/check-auth-code`, body, true);
|
|
799
958
|
}
|
|
959
|
+
/**
|
|
960
|
+
* 企业实名认证:服务端把企业证件、法人、认证方式与完整注册地址一并校验,
|
|
961
|
+
* 因此这里整体组装后再发出,缺字段时由命令层提前拦截而不是靠服务端报错。
|
|
962
|
+
*/
|
|
800
963
|
async startOrganRealNameAuth(request) {
|
|
801
964
|
const body = {
|
|
802
965
|
name: request.name,
|
|
803
966
|
certNumber: request.certNumber,
|
|
967
|
+
certType: request.certType,
|
|
968
|
+
authType: request.authType,
|
|
969
|
+
agentAccountId: request.agentAccountId,
|
|
970
|
+
legalName: request.legalName,
|
|
971
|
+
countryCode: request.countryCode,
|
|
972
|
+
alpha2Code: request.alpha2Code,
|
|
973
|
+
country: request.country,
|
|
974
|
+
state: request.state,
|
|
975
|
+
city: request.city,
|
|
976
|
+
county: request.county,
|
|
977
|
+
address: request.address,
|
|
804
978
|
};
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
979
|
+
// 服务端 certTypeEnum 与 certType 是同一含义的两种写法,由 certType 推导可
|
|
980
|
+
// 避免两者互相矛盾;超出枚举范围时留空,服务端允许 null。
|
|
981
|
+
const certTypeEnum = request.certTypeEnum ?? ORGAN_CERT_TYPE_ENUMS[request.certType];
|
|
982
|
+
if (certTypeEnum)
|
|
983
|
+
body.certTypeEnum = certTypeEnum;
|
|
984
|
+
if (request.legalCertNumber)
|
|
985
|
+
body.legalCertNumber = request.legalCertNumber;
|
|
986
|
+
if (request.legalCertType != null)
|
|
987
|
+
body.legalCertType = request.legalCertType;
|
|
988
|
+
return this.postJson(`${API_PREFIX}/onboarding/organ-auth`, body, true);
|
|
810
989
|
}
|
|
811
990
|
async openBrandAndApplyContract(request) {
|
|
812
991
|
const body = {
|
|
@@ -828,7 +1007,14 @@ export class ApiClient {
|
|
|
828
1007
|
body.county = request.county;
|
|
829
1008
|
if (request.cLegalName)
|
|
830
1009
|
body.cLegalName = request.cLegalName;
|
|
831
|
-
return this.
|
|
1010
|
+
return this.postJson(`${API_PREFIX}/onboarding/open-brand`, body, true);
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* 重复入驻场景下通知当前客户对应的销售(钉钉卡片)。
|
|
1014
|
+
* 客户编码由服务端从 API Key 上下文取,通知内容服务端写死,本接口无入参、无返回数据。
|
|
1015
|
+
*/
|
|
1016
|
+
async notifyRepeatEnterSaler() {
|
|
1017
|
+
await this.postJson(`${API_PREFIX}/onboarding/repeat-enter/notify-saler`, {}, true);
|
|
832
1018
|
}
|
|
833
1019
|
async authSaveGAT(request) {
|
|
834
1020
|
const body = {
|
|
@@ -865,11 +1051,7 @@ export class ApiClient {
|
|
|
865
1051
|
body.countyCode = request.countyCode;
|
|
866
1052
|
if (request.address)
|
|
867
1053
|
body.address = request.address;
|
|
868
|
-
return this.
|
|
869
|
-
}
|
|
870
|
-
/** amount is in cents — the backend rejects yuan. */
|
|
871
|
-
async checkRemitAmount(flowId, amount) {
|
|
872
|
-
return this.post(`${API_PREFIX}/onboarding/check-remit`, { flowId, amount }, true);
|
|
1054
|
+
return this.postJson(`${API_PREFIX}/onboarding/gat-auth`, body, true);
|
|
873
1055
|
}
|
|
874
1056
|
async queryOnboardingProgress() {
|
|
875
1057
|
return this.get(`${API_PREFIX}/onboarding/progress`, {}, true);
|
|
@@ -894,8 +1076,12 @@ export class ApiClient {
|
|
|
894
1076
|
body.pageSize = pageSize;
|
|
895
1077
|
return this.post(`${API_PREFIX}/onboarding/contracts`, body, true);
|
|
896
1078
|
}
|
|
897
|
-
|
|
898
|
-
|
|
1079
|
+
/**
|
|
1080
|
+
* `status` 必填,取自 `contract list`:服务端会重新查合同详情并以实际状态为准,
|
|
1081
|
+
* 详情未返回状态时用它兜底,所以状态码只能来自列表,不能猜。
|
|
1082
|
+
*/
|
|
1083
|
+
async getContractSignUrl(contractCode, status) {
|
|
1084
|
+
return this.post(`${API_PREFIX}/onboarding/contract-sign-url`, { contractCode, status }, true);
|
|
899
1085
|
}
|
|
900
1086
|
}
|
|
901
1087
|
//# sourceMappingURL=api-client.js.map
|