@be-link/pos-cli-nodejs 1.0.237 → 1.0.239
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 +43 -0
- package/index.d.ts +8 -0
- package/index.js +12 -1
- package/package.json +1 -1
- package/pos/modules/orderQuery/advisor.d.ts +14 -0
- package/pos/modules/orderQuery/advisor.js +111 -0
- package/pos/modules/orderQuery/advisorBusinessError.d.ts +8 -0
- package/pos/modules/orderQuery/advisorBusinessError.js +37 -0
- package/pos/modules/orderQuery/advisorHttpSender.d.ts +7 -0
- package/pos/modules/orderQuery/advisorHttpSender.js +138 -0
- package/pos/modules/orderQuery/advisorProtection.d.ts +63 -0
- package/pos/modules/orderQuery/advisorProtection.js +262 -0
- package/pos/modules/orderQuery/advisorTypes.d.ts +80 -0
- package/pos/modules/orderQuery/advisorTypes.js +2 -0
- package/pos/http.test.d.ts +0 -1
- package/pos/http.test.js +0 -102
- package/pos/modules/orderAbnormalFollow/service.test.d.ts +0 -1
- package/pos/modules/orderAbnormalFollow/service.test.js +0 -72
- package/pos/modules/orderCore/seniorDisclaimer.test.d.ts +0 -1
- package/pos/modules/orderCore/seniorDisclaimer.test.js +0 -43
- package/pos/modules/orderCore/service.test.d.ts +0 -1
- package/pos/modules/orderCore/service.test.js +0 -72
- package/pos/modules/orderQuery/service.test.d.ts +0 -1
- package/pos/modules/orderQuery/service.test.js +0 -71
- package/pos/modules/orderQuery/settlement.test.d.ts +0 -1
- package/pos/modules/orderQuery/settlement.test.js +0 -43
- package/pos/modules/orderRoomShare/service.test.d.ts +0 -1
- package/pos/modules/orderRoomShare/service.test.js +0 -103
- package/types.test.d.ts +0 -1
- package/types.test.js +0 -55
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createSignedAdvisorTransport = exports.createAdvisorRequestVerifier = exports.createAdvisorRequestSigner = exports.canonicalAdvisorJson = exports.advisorActionPaths = void 0;
|
|
13
|
+
const crypto_1 = require("crypto");
|
|
14
|
+
const advisorBusinessError_1 = require("./advisorBusinessError");
|
|
15
|
+
exports.advisorActionPaths = {
|
|
16
|
+
queryAdvisorShareOrderList: '/query/user/query-advisor-share-order-list',
|
|
17
|
+
getAdvisorShareOrderFacts: '/query/user/get-advisor-share-order-facts',
|
|
18
|
+
getAdvisorShareOrderFactsBatch: '/query/user/get-advisor-share-order-facts-batch',
|
|
19
|
+
getAdvisorShareAdjustmentCreateFacts: '/query/user/get-advisor-share-adjustment-create-facts',
|
|
20
|
+
};
|
|
21
|
+
function reject() { throw new Error('ADVISOR_PROTECTED_REQUEST_REJECTED'); }
|
|
22
|
+
function object(value) {
|
|
23
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) ||
|
|
24
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
25
|
+
reject();
|
|
26
|
+
if (Object.getOwnPropertySymbols(value).length)
|
|
27
|
+
reject();
|
|
28
|
+
for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(value))) {
|
|
29
|
+
if (!descriptor.enumerable || !('value' in descriptor))
|
|
30
|
+
reject();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function text(value) {
|
|
34
|
+
if (typeof value !== 'string' || !value || value.trim() !== value)
|
|
35
|
+
reject();
|
|
36
|
+
}
|
|
37
|
+
function exact(value, keys) {
|
|
38
|
+
if (Object.keys(value).sort().join(',') !== keys.sort().join(','))
|
|
39
|
+
reject();
|
|
40
|
+
}
|
|
41
|
+
function actor(value) {
|
|
42
|
+
object(value);
|
|
43
|
+
exact(value, ['advisorId', 'name']);
|
|
44
|
+
text(value.advisorId);
|
|
45
|
+
if (typeof value.name !== 'string')
|
|
46
|
+
reject();
|
|
47
|
+
return Object.freeze({ advisorId: value.advisorId, name: value.name });
|
|
48
|
+
}
|
|
49
|
+
function caller(value) {
|
|
50
|
+
if (value !== 'DistributionService' && value !== 'VitalityService')
|
|
51
|
+
reject();
|
|
52
|
+
}
|
|
53
|
+
// Protocol v1: plain JSON only, UTF-16 key ordering, no omitted/undefined values,
|
|
54
|
+
// accessors, symbols, sparse arrays, cycles, non-finite numbers or unsafe integers.
|
|
55
|
+
function canonicalAdvisorJson(input) {
|
|
56
|
+
const ancestors = new Set();
|
|
57
|
+
function visit(value) {
|
|
58
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean')
|
|
59
|
+
return JSON.stringify(value);
|
|
60
|
+
if (typeof value === 'number') {
|
|
61
|
+
if (!Number.isFinite(value) || (Number.isInteger(value) && !Number.isSafeInteger(value)))
|
|
62
|
+
reject();
|
|
63
|
+
return JSON.stringify(value);
|
|
64
|
+
}
|
|
65
|
+
if (typeof value !== 'object' || !value || ancestors.has(value))
|
|
66
|
+
reject();
|
|
67
|
+
ancestors.add(value);
|
|
68
|
+
try {
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
if (Object.getOwnPropertySymbols(value).length ||
|
|
71
|
+
Object.keys(value).length !== value.length)
|
|
72
|
+
reject();
|
|
73
|
+
const entries = [];
|
|
74
|
+
for (let i = 0; i < value.length; i++) {
|
|
75
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
|
|
76
|
+
if (!(descriptor === null || descriptor === void 0 ? void 0 : descriptor.enumerable) || !('value' in descriptor))
|
|
77
|
+
reject();
|
|
78
|
+
entries.push(visit(descriptor.value));
|
|
79
|
+
}
|
|
80
|
+
return `[${entries.join(',')}]`;
|
|
81
|
+
}
|
|
82
|
+
object(value);
|
|
83
|
+
return `{${Object.keys(value).sort().map((key) => {
|
|
84
|
+
var _a;
|
|
85
|
+
if (['__proto__', 'constructor', 'prototype'].includes(key))
|
|
86
|
+
reject();
|
|
87
|
+
return `${JSON.stringify(key)}:${visit((_a = Object.getOwnPropertyDescriptor(value, key)) === null || _a === void 0 ? void 0 : _a.value)}`;
|
|
88
|
+
}).join(',')}}`;
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
ancestors.delete(value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return visit(input);
|
|
95
|
+
}
|
|
96
|
+
exports.canonicalAdvisorJson = canonicalAdvisorJson;
|
|
97
|
+
function digest(body) {
|
|
98
|
+
return (0, crypto_1.createHash)('sha256').update(canonicalAdvisorJson(body), 'utf8').digest('hex');
|
|
99
|
+
}
|
|
100
|
+
function actionAllowed(issuer, action) {
|
|
101
|
+
if (!Object.prototype.hasOwnProperty.call(exports.advisorActionPaths, action))
|
|
102
|
+
reject();
|
|
103
|
+
if (action === 'queryAdvisorShareOrderList' && issuer !== 'DistributionService')
|
|
104
|
+
reject();
|
|
105
|
+
if (action === 'getAdvisorShareAdjustmentCreateFacts' && issuer !== 'VitalityService')
|
|
106
|
+
reject();
|
|
107
|
+
}
|
|
108
|
+
function requestCheck(request, issuer, identity) {
|
|
109
|
+
actionAllowed(issuer, request.action);
|
|
110
|
+
if (request.method !== 'POST' || request.path !== exports.advisorActionPaths[request.action])
|
|
111
|
+
reject();
|
|
112
|
+
object(request.body);
|
|
113
|
+
if (request.body.advisorId !== identity.advisorId)
|
|
114
|
+
reject();
|
|
115
|
+
}
|
|
116
|
+
function seconds(now) {
|
|
117
|
+
const value = now();
|
|
118
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
119
|
+
reject();
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
function encode(value) { return value.toString('base64url'); }
|
|
123
|
+
function decode(value) {
|
|
124
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value))
|
|
125
|
+
reject();
|
|
126
|
+
const result = Buffer.from(value, 'base64url');
|
|
127
|
+
if (encode(result) !== value)
|
|
128
|
+
reject();
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
function keyCheck(key, type) {
|
|
132
|
+
if (!(key instanceof crypto_1.KeyObject) || key.type !== type || key.asymmetricKeyType !== 'ed25519')
|
|
133
|
+
reject();
|
|
134
|
+
}
|
|
135
|
+
function createAdvisorRequestSigner(config) {
|
|
136
|
+
caller(config === null || config === void 0 ? void 0 : config.issuer);
|
|
137
|
+
text(config.kid);
|
|
138
|
+
text(config.audience);
|
|
139
|
+
text(config.environment);
|
|
140
|
+
keyCheck(config.privateKey, 'private');
|
|
141
|
+
if (!Number.isSafeInteger(config.ttlSeconds) || config.ttlSeconds < 1 || typeof config.now !== 'function')
|
|
142
|
+
reject();
|
|
143
|
+
// Capture all configuration and key material before any asynchronous sender runs.
|
|
144
|
+
const { issuer, kid, audience, environment, ttlSeconds, now } = config;
|
|
145
|
+
const key = (0, crypto_1.createPrivateKey)({ key: config.privateKey.export({ format: 'der', type: 'pkcs8' }), format: 'der', type: 'pkcs8' });
|
|
146
|
+
return (request, identity) => {
|
|
147
|
+
const identitySnapshot = actor(identity);
|
|
148
|
+
requestCheck(request, issuer, identitySnapshot);
|
|
149
|
+
const iat = seconds(now);
|
|
150
|
+
const exp = iat + ttlSeconds;
|
|
151
|
+
if (!Number.isSafeInteger(exp))
|
|
152
|
+
reject();
|
|
153
|
+
const payload = {
|
|
154
|
+
version: 1, kid, issuer, audience, environment, actor: identitySnapshot,
|
|
155
|
+
action: request.action, method: request.method, path: request.path,
|
|
156
|
+
bodyDigest: digest(request.body), iat, exp,
|
|
157
|
+
};
|
|
158
|
+
const value = `v1.${encode(Buffer.from(canonicalAdvisorJson(payload)))}`;
|
|
159
|
+
return `${value}.${encode((0, crypto_1.sign)(null, Buffer.from(`pos:advisor-request:${value}`), key))}`;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
exports.createAdvisorRequestSigner = createAdvisorRequestSigner;
|
|
163
|
+
function createAdvisorRequestVerifier(config) {
|
|
164
|
+
text(config === null || config === void 0 ? void 0 : config.audience);
|
|
165
|
+
text(config.environment);
|
|
166
|
+
if (!Number.isSafeInteger(config.maxTtlSeconds) || config.maxTtlSeconds < 1 ||
|
|
167
|
+
!Number.isSafeInteger(config.clockSkewSeconds) || config.clockSkewSeconds < 0 ||
|
|
168
|
+
typeof config.now !== 'function' || !Array.isArray(config.keys) || !config.keys.length)
|
|
169
|
+
reject();
|
|
170
|
+
const { audience, environment, maxTtlSeconds, clockSkewSeconds, now } = config;
|
|
171
|
+
const trusted = new Map();
|
|
172
|
+
for (const entry of config.keys) {
|
|
173
|
+
caller(entry === null || entry === void 0 ? void 0 : entry.issuer);
|
|
174
|
+
text(entry.kid);
|
|
175
|
+
keyCheck(entry.publicKey, 'public');
|
|
176
|
+
const key = JSON.stringify([entry.issuer, entry.kid]);
|
|
177
|
+
if (trusted.has(key))
|
|
178
|
+
reject();
|
|
179
|
+
trusted.set(key, (0, crypto_1.createPublicKey)({ key: entry.publicKey.export({ format: 'der', type: 'spki' }), format: 'der', type: 'spki' }));
|
|
180
|
+
}
|
|
181
|
+
return (request, assertion) => {
|
|
182
|
+
try {
|
|
183
|
+
if (typeof assertion !== 'string')
|
|
184
|
+
reject();
|
|
185
|
+
const parts = assertion.split('.');
|
|
186
|
+
if (parts.length !== 3 || parts[0] !== 'v1')
|
|
187
|
+
reject();
|
|
188
|
+
const serialized = decode(parts[1]).toString('utf8');
|
|
189
|
+
const claim = JSON.parse(serialized);
|
|
190
|
+
object(claim);
|
|
191
|
+
exact(claim, ['version', 'kid', 'issuer', 'audience', 'environment', 'actor', 'action', 'method', 'path', 'bodyDigest', 'iat', 'exp']);
|
|
192
|
+
if (canonicalAdvisorJson(claim) !== serialized)
|
|
193
|
+
reject();
|
|
194
|
+
caller(claim.issuer);
|
|
195
|
+
text(claim.kid);
|
|
196
|
+
const key = trusted.get(JSON.stringify([claim.issuer, claim.kid]));
|
|
197
|
+
if (!key || !(0, crypto_1.verify)(null, Buffer.from(`pos:advisor-request:${parts[0]}.${parts[1]}`), key, decode(parts[2])))
|
|
198
|
+
reject();
|
|
199
|
+
if (claim.version !== 1 || claim.audience !== audience || claim.environment !== environment)
|
|
200
|
+
reject();
|
|
201
|
+
const current = seconds(now);
|
|
202
|
+
if (typeof claim.iat !== 'number' || typeof claim.exp !== 'number' ||
|
|
203
|
+
!Number.isSafeInteger(claim.iat) || !Number.isSafeInteger(claim.exp) || claim.iat < 0 ||
|
|
204
|
+
claim.exp <= claim.iat || claim.exp - claim.iat > maxTtlSeconds ||
|
|
205
|
+
claim.iat > current + clockSkewSeconds || claim.exp <= current - clockSkewSeconds)
|
|
206
|
+
reject();
|
|
207
|
+
const identity = actor(claim.actor);
|
|
208
|
+
requestCheck(request, claim.issuer, identity);
|
|
209
|
+
if (claim.action !== request.action || claim.method !== request.method || claim.path !== request.path ||
|
|
210
|
+
claim.bodyDigest !== digest(request.body))
|
|
211
|
+
reject();
|
|
212
|
+
return Object.freeze({ issuer: claim.issuer, actor: identity, action: request.action, expiresAt: claim.exp });
|
|
213
|
+
}
|
|
214
|
+
catch (_a) {
|
|
215
|
+
return reject();
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
exports.createAdvisorRequestVerifier = createAdvisorRequestVerifier;
|
|
220
|
+
function createSignedAdvisorTransport(options) {
|
|
221
|
+
if (typeof (options === null || options === void 0 ? void 0 : options.signer) !== 'function' || typeof options.send !== 'function')
|
|
222
|
+
reject();
|
|
223
|
+
const endpoint = new URL(options.origin);
|
|
224
|
+
if (!['https:', 'http:'].includes(endpoint.protocol) || endpoint.username || endpoint.password ||
|
|
225
|
+
endpoint.search || endpoint.hash || endpoint.pathname !== '/')
|
|
226
|
+
reject();
|
|
227
|
+
const origin = endpoint.origin;
|
|
228
|
+
const identity = actor(options.actor);
|
|
229
|
+
const { signer, send } = options;
|
|
230
|
+
return (request) => __awaiter(this, void 0, void 0, function* () {
|
|
231
|
+
var _a;
|
|
232
|
+
const path = exports.advisorActionPaths[request.action];
|
|
233
|
+
// Services provide an exact protocol path; only this transport selects the destination.
|
|
234
|
+
// Absolute URLs remain accepted for existing direct callers, but may not change origin.
|
|
235
|
+
if (typeof path !== 'string' || (request.url.startsWith('/') && request.url !== path))
|
|
236
|
+
reject();
|
|
237
|
+
const url = new URL(request.url, origin);
|
|
238
|
+
if (url.origin !== origin || url.search || url.hash || url.username || url.password ||
|
|
239
|
+
url.pathname !== path)
|
|
240
|
+
reject();
|
|
241
|
+
// Serialize then parse a private snapshot: the sender receives no caller-owned references.
|
|
242
|
+
const body = JSON.parse(canonicalAdvisorJson(request.body));
|
|
243
|
+
const assertion = signer({ method: request.method, path: url.pathname, action: request.action, body }, identity);
|
|
244
|
+
try {
|
|
245
|
+
return yield send(Object.freeze({ method: 'POST', url: url.href, body: deepFreeze(body),
|
|
246
|
+
headers: Object.freeze({ 'x-advisor-assertion': assertion }) }));
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
// Rebuild only locally branded rejection codes; HTTP errors may embed signed headers.
|
|
250
|
+
throw (_a = (0, advisorBusinessError_1.copyAdvisorBusinessError)(error)) !== null && _a !== void 0 ? _a : new Error('ADVISOR_PROTECTED_TRANSPORT_FAILED');
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
exports.createSignedAdvisorTransport = createSignedAdvisorTransport;
|
|
255
|
+
function deepFreeze(value) {
|
|
256
|
+
if (value && typeof value === 'object') {
|
|
257
|
+
for (const child of Object.values(value))
|
|
258
|
+
deepFreeze(child);
|
|
259
|
+
Object.freeze(value);
|
|
260
|
+
}
|
|
261
|
+
return value;
|
|
262
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type AdvisorOrderFacts = {
|
|
2
|
+
orderId: string;
|
|
3
|
+
orderNo: string;
|
|
4
|
+
ownerAdvisorId: string;
|
|
5
|
+
status: string;
|
|
6
|
+
categoryOne: string;
|
|
7
|
+
categoryTwo: string;
|
|
8
|
+
rootOrderId: string;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
itemName: string;
|
|
11
|
+
serviceStartAt: number | null;
|
|
12
|
+
serviceEndAt: number | null;
|
|
13
|
+
/** 小管家首个有效 ITEM SKU 的剩余份数,不是差价份数。 */
|
|
14
|
+
quantity: number;
|
|
15
|
+
/** 小管家“合计”:原单 remainFee,不是实付累计。 */
|
|
16
|
+
remainingFeeCent: number;
|
|
17
|
+
contactName: string;
|
|
18
|
+
maskedPhone: string | null;
|
|
19
|
+
};
|
|
20
|
+
export type AdvisorOrderListRequest = {
|
|
21
|
+
advisorId: string;
|
|
22
|
+
statusList?: string[];
|
|
23
|
+
categoryTwo?: string;
|
|
24
|
+
keyword?: string;
|
|
25
|
+
cursor?: string;
|
|
26
|
+
pageSize?: number;
|
|
27
|
+
};
|
|
28
|
+
export type AdvisorOrderRequest = {
|
|
29
|
+
advisorId: string;
|
|
30
|
+
orderId: string;
|
|
31
|
+
};
|
|
32
|
+
export type AdvisorOrderBatchRequest = {
|
|
33
|
+
advisorId: string;
|
|
34
|
+
orderIds: string[];
|
|
35
|
+
};
|
|
36
|
+
export type AdvisorOrderPage = {
|
|
37
|
+
list: AdvisorOrderFacts[];
|
|
38
|
+
nextCursor: string | null;
|
|
39
|
+
hasMore: boolean;
|
|
40
|
+
};
|
|
41
|
+
/** 仅可信 VS 创建调用方可读;不是预支付授权。 */
|
|
42
|
+
export type AdvisorAdjustmentCreateFacts = {
|
|
43
|
+
order: AdvisorOrderFacts;
|
|
44
|
+
buyerUnionId: string;
|
|
45
|
+
buyerOpenId: string;
|
|
46
|
+
privateButlerUnionId: string | null;
|
|
47
|
+
/** 原 attributes 生成布尔列;VS 沿小管家规则仅 true 时检查同程绑定。 */
|
|
48
|
+
isTongchengVideoAccountOrder: boolean;
|
|
49
|
+
};
|
|
50
|
+
export interface AdvisorQueryController {
|
|
51
|
+
queryAdvisorShareOrderList(request: AdvisorOrderListRequest): Promise<AdvisorOrderPage>;
|
|
52
|
+
getAdvisorShareOrderFacts(request: AdvisorOrderRequest): Promise<AdvisorOrderFacts>;
|
|
53
|
+
getAdvisorShareOrderFactsBatch(request: AdvisorOrderBatchRequest): Promise<{
|
|
54
|
+
list: AdvisorOrderFacts[];
|
|
55
|
+
}>;
|
|
56
|
+
getAdvisorShareAdjustmentCreateFacts(request: AdvisorOrderRequest): Promise<AdvisorAdjustmentCreateFacts>;
|
|
57
|
+
}
|
|
58
|
+
export type AdvisorQueryMethod = keyof AdvisorQueryController;
|
|
59
|
+
export type AdvisorProtectedRequest = Readonly<{
|
|
60
|
+
method: 'POST';
|
|
61
|
+
url: string;
|
|
62
|
+
action: AdvisorQueryMethod;
|
|
63
|
+
body: Readonly<Record<string, unknown>>;
|
|
64
|
+
}>;
|
|
65
|
+
/** POS 宿主封装,不使用 DS 的 code/SUCCESS 封装。 */
|
|
66
|
+
export type AdvisorResponseEnvelope<T> = {
|
|
67
|
+
data: T;
|
|
68
|
+
message: string;
|
|
69
|
+
success?: boolean;
|
|
70
|
+
requestId?: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Trusted application integration boundary, not a credential or caller assertion.
|
|
74
|
+
* The implementation must authenticate the caller and bind actor, action, full
|
|
75
|
+
* body, destination and expiry under the agreed POS protocol. It must also
|
|
76
|
+
* sanitize errors/logs. createSignedAdvisorTransport implements protocol v1;
|
|
77
|
+
* real sender, trust configuration and protected POS routes still require assembly.
|
|
78
|
+
* A shared inside token or a client-supplied caller name is not sufficient.
|
|
79
|
+
*/
|
|
80
|
+
export type AdvisorProtectedTransport = (request: AdvisorProtectedRequest) => Promise<AdvisorResponseEnvelope<unknown>>;
|
package/pos/http.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/pos/http.test.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
-
};
|
|
37
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
const mockPost = jest.fn();
|
|
39
|
-
jest.mock('axios', () => ({
|
|
40
|
-
__esModule: true,
|
|
41
|
-
default: {
|
|
42
|
-
create: jest.fn(() => ({ post: mockPost })),
|
|
43
|
-
},
|
|
44
|
-
}));
|
|
45
|
-
jest.mock('axios-retry', () => ({ __esModule: true, default: jest.fn() }));
|
|
46
|
-
jest.mock('uuid', () => ({ v4: jest.fn(() => 'request-id') }));
|
|
47
|
-
const BizError_1 = __importDefault(require("../errors/BizError"));
|
|
48
|
-
const SystemError_1 = __importDefault(require("../errors/SystemError"));
|
|
49
|
-
const http_1 = require("./http");
|
|
50
|
-
describe('callApi error envelope', () => {
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
mockPost.mockReset();
|
|
53
|
-
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
54
|
-
});
|
|
55
|
-
afterEach(() => {
|
|
56
|
-
jest.restoreAllMocks();
|
|
57
|
-
});
|
|
58
|
-
it('HTTP 400 透传稳定业务 code,同时保留 statusCode', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
-
mockPost.mockRejectedValue({
|
|
60
|
-
message: 'Request failed',
|
|
61
|
-
stack: 'stack',
|
|
62
|
-
response: {
|
|
63
|
-
status: 400,
|
|
64
|
-
data: { data: null, message: '拼房功能未开启', code: 'ROOM_SHARE_DISABLED' },
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
const error = yield (0, http_1.callApi)('https://pos.test/room-share/save', {}).catch((caught) => caught);
|
|
68
|
-
expect(error).toBeInstanceOf(BizError_1.default);
|
|
69
|
-
expect(error).toMatchObject({ code: 'ROOM_SHARE_DISABLED', statusCode: 400 });
|
|
70
|
-
}));
|
|
71
|
-
it('旧 400 envelope 无业务 code 时保持数字 code=400', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
72
|
-
mockPost.mockRejectedValue({
|
|
73
|
-
message: 'Request failed',
|
|
74
|
-
stack: 'stack',
|
|
75
|
-
response: { status: 400, data: { data: null, message: 'legacy error' } },
|
|
76
|
-
});
|
|
77
|
-
const error = yield (0, http_1.callApi)('https://pos.test/core/legacy', {}).catch((caught) => caught);
|
|
78
|
-
expect(error).toBeInstanceOf(BizError_1.default);
|
|
79
|
-
expect(error).toMatchObject({ code: 400, statusCode: 400 });
|
|
80
|
-
}));
|
|
81
|
-
it('非 400 响应仍返回 SystemError 和原 HTTP code', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
82
|
-
mockPost.mockRejectedValue({
|
|
83
|
-
message: 'Request failed',
|
|
84
|
-
stack: 'stack',
|
|
85
|
-
response: {
|
|
86
|
-
status: 503,
|
|
87
|
-
data: { data: null, message: 'unavailable', code: 'IGNORED_CODE' },
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
const error = yield (0, http_1.callApi)('https://pos.test/core/query', {}).catch((caught) => caught);
|
|
91
|
-
expect(error).toBeInstanceOf(SystemError_1.default);
|
|
92
|
-
expect(error).toMatchObject({ code: 503 });
|
|
93
|
-
}));
|
|
94
|
-
});
|
|
95
|
-
it('高龄拦截码与可供旧客户端展示的完整恢复文案透传', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
96
|
-
const { SENIOR_CONFIRMATION_REQUIRED, SENIOR_CONFIRMATION_RECOVERY_MESSAGE } = yield Promise.resolve().then(() => __importStar(require('./modules/orderCore/seniorConfirmationRecovery')));
|
|
97
|
-
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
98
|
-
mockPost.mockRejectedValue({ message: 'Request failed', response: { status: 400, data: { message: SENIOR_CONFIRMATION_RECOVERY_MESSAGE, code: SENIOR_CONFIRMATION_REQUIRED } } });
|
|
99
|
-
const error = yield (0, http_1.callApi)('https://pos.test/core/accept', {}).catch(caught => caught);
|
|
100
|
-
expect(error).toMatchObject({ code: SENIOR_CONFIRMATION_REQUIRED, message: SENIOR_CONFIRMATION_RECOVERY_MESSAGE, statusCode: 400 });
|
|
101
|
-
jest.restoreAllMocks();
|
|
102
|
-
}));
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
jest.mock('../../http', () => ({
|
|
16
|
-
callApi: jest.fn(),
|
|
17
|
-
}));
|
|
18
|
-
const http_1 = require("../../http");
|
|
19
|
-
const service_1 = __importDefault(require("./service"));
|
|
20
|
-
const mockedCallApi = http_1.callApi;
|
|
21
|
-
describe('OrderAbnormalFollowService', () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
mockedCallApi.mockReset();
|
|
24
|
-
mockedCallApi.mockResolvedValue(undefined);
|
|
25
|
-
});
|
|
26
|
-
it.each([
|
|
27
|
-
['upsertFollow', { orderId: 'order-1', abnormalType: 'REJECTED' }, 'upsert-follow'],
|
|
28
|
-
['listByOrderIds', { orderIds: ['order-1'] }, 'list-by-order-ids'],
|
|
29
|
-
[
|
|
30
|
-
'listActiveByFollowUnionIds',
|
|
31
|
-
{ followUnionIds: ['sales-1'], pageIndex: 0, pageSize: 20 },
|
|
32
|
-
'list-active-by-follow-union-ids',
|
|
33
|
-
],
|
|
34
|
-
[
|
|
35
|
-
'markUserReminded',
|
|
36
|
-
{
|
|
37
|
-
orderId: 'order-1',
|
|
38
|
-
abnormalType: 'HOTEL_ORDER_CHANGE',
|
|
39
|
-
followUnionId: 'sales-1',
|
|
40
|
-
changeFlowId: 'flow-1',
|
|
41
|
-
},
|
|
42
|
-
'mark-user-reminded',
|
|
43
|
-
],
|
|
44
|
-
[
|
|
45
|
-
'createHotelChangeFollow',
|
|
46
|
-
{
|
|
47
|
-
orderId: 'order-1',
|
|
48
|
-
followUnionId: 'sales-1',
|
|
49
|
-
changeFlowId: 'flow-1',
|
|
50
|
-
changeType: 'CHANGE_RESCHEDULE_ROOM',
|
|
51
|
-
changeStatus: 'SUCCEED',
|
|
52
|
-
changeContent: '改期',
|
|
53
|
-
processedAt: 1,
|
|
54
|
-
},
|
|
55
|
-
'create-hotel-change-follow',
|
|
56
|
-
],
|
|
57
|
-
[
|
|
58
|
-
'softDelete',
|
|
59
|
-
{
|
|
60
|
-
orderId: 'order-1',
|
|
61
|
-
abnormalType: 'HOTEL_ORDER_CHANGE',
|
|
62
|
-
followUnionId: 'sales-1',
|
|
63
|
-
changeFlowId: 'flow-1',
|
|
64
|
-
},
|
|
65
|
-
'soft-delete',
|
|
66
|
-
],
|
|
67
|
-
['countByTypeInScope', { followUnionIds: ['sales-1'], abnormalType: 'REJECTED' }, 'count-by-type-in-scope'],
|
|
68
|
-
])('%s 映射到 abnormal-follow kebab URL 并原样序列化请求', (method, request, path) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
-
yield service_1.default[method](request);
|
|
70
|
-
expect(mockedCallApi).toHaveBeenCalledWith(expect.stringMatching(new RegExp(`/abnormal-follow/${path}$`)), request);
|
|
71
|
-
}));
|
|
72
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
jest.mock('../../http', () => ({ callApi: jest.fn() }));
|
|
16
|
-
const http_1 = require("../../http");
|
|
17
|
-
const service_1 = __importDefault(require("./service"));
|
|
18
|
-
it('确认记录两个接口保持完整请求和 kebab-case 路由', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
-
const prepare = { version: 'V1', orderRequest: { tourismInfo: { touristIdList: ['senior'] } } };
|
|
20
|
-
yield service_1.default.prepareSeniorDisclaimer(prepare);
|
|
21
|
-
expect(http_1.callApi).toHaveBeenCalledWith(expect.stringMatching(/\/core\/prepare-senior-disclaimer$/), prepare);
|
|
22
|
-
const accept = { recordId: 'record-A', unionId: 'buyer-A' };
|
|
23
|
-
yield service_1.default.acceptSeniorDisclaimer(accept);
|
|
24
|
-
expect(http_1.callApi).toHaveBeenCalledWith(expect.stringMatching(/\/core\/accept-senior-disclaimer$/), accept);
|
|
25
|
-
}));
|
|
26
|
-
for (const [name, route] of [
|
|
27
|
-
['getSeniorConfirmation', 'get-senior-confirmation'],
|
|
28
|
-
['saveSeniorHealthDraft', 'save-senior-health-draft'],
|
|
29
|
-
['markSeniorConfirmationRead', 'mark-senior-confirmation-read'],
|
|
30
|
-
['acceptSeniorConfirmation', 'accept-senior-confirmation'],
|
|
31
|
-
]) {
|
|
32
|
-
it(name + ' preserves request and route', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
-
const request = { orderId: 'order-A', unionId: 'buyer-A', sessionId: 'session-A', touristId: 'person-A' };
|
|
34
|
-
yield service_1.default[name](request);
|
|
35
|
-
expect(http_1.callApi).toHaveBeenLastCalledWith(expect.stringMatching(new RegExp('/core/' + route + '$')), request);
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
38
|
-
it('START的服务端正文及版本保持透传,不只保留token', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
-
const reply = { readToken: 'reading-token', readText: '合成协议正文', documentVersion: 'approved-version' };
|
|
40
|
-
http_1.callApi.mockResolvedValue(reply);
|
|
41
|
-
const result = yield service_1.default.markSeniorConfirmationRead({ orderId: 'order-A', unionId: 'buyer-A', sessionId: 'session-A', touristId: 'person-A', kind: 'HEALTH', phase: 'START' });
|
|
42
|
-
expect(result).toEqual(reply);
|
|
43
|
-
}));
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
const mockCallApi = jest.fn();
|
|
16
|
-
jest.mock('../../http', () => ({
|
|
17
|
-
callApi: (...args) => mockCallApi(...args),
|
|
18
|
-
}));
|
|
19
|
-
const service_1 = __importDefault(require("./service"));
|
|
20
|
-
describe('OrderCoreService.sceneInfo advisorShare passthrough', () => {
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
mockCallApi.mockReset().mockResolvedValue({ tradeOrderId: 'order-1' });
|
|
23
|
-
});
|
|
24
|
-
const sceneInfo = {
|
|
25
|
-
scene1: '',
|
|
26
|
-
scene2: '',
|
|
27
|
-
appScene: 0,
|
|
28
|
-
advisorShare: { advisorId: '', cardId: 'card-1', shareAt: 0 },
|
|
29
|
-
};
|
|
30
|
-
it.each([
|
|
31
|
-
['省略', undefined],
|
|
32
|
-
['空对象', {}],
|
|
33
|
-
['空字符串', { advisorId: '' }],
|
|
34
|
-
['零时间戳', { shareAt: 0 }],
|
|
35
|
-
['完整对象', { advisorId: 'advisor-1', cardId: 'card-1', shareAt: 123 }],
|
|
36
|
-
])('orderCreate 透传 %s advisorShare', (_label, advisorShare) => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
|
-
const request = {
|
|
38
|
-
basicInfo: {
|
|
39
|
-
sceneInfo: Object.assign({ scene1: '', scene2: '', appScene: 0 }, (advisorShare === undefined ? {} : { advisorShare })),
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
yield service_1.default.orderCreate(request);
|
|
43
|
-
expect(mockCallApi).toHaveBeenCalledWith(expect.stringMatching(/\/core\/order-create$/), request);
|
|
44
|
-
expect(mockCallApi.mock.calls[0][1].basicInfo.sceneInfo.advisorShare).toBe(advisorShare);
|
|
45
|
-
}));
|
|
46
|
-
it.each([
|
|
47
|
-
['orderCreate', 'order-create', false],
|
|
48
|
-
['storeOrderCreate', 'store-order-create', false],
|
|
49
|
-
['orderCreateV2', 'order-create-v2', false],
|
|
50
|
-
['depositOrderCreate', 'deposit-order-create', false],
|
|
51
|
-
['depositOrderComplete', 'deposit-order-complete', true],
|
|
52
|
-
])('%s 原样透传分享信息', (method, path, isComplete) => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
-
const request = Object.assign({ basicInfo: { sceneInfo } }, (isComplete ? { depositOrderId: 'deposit-1' } : {}));
|
|
54
|
-
yield service_1.default[method](request);
|
|
55
|
-
expect(mockCallApi).toHaveBeenCalledWith(expect.stringMatching(new RegExp(`/core/${path}$`)), request);
|
|
56
|
-
expect(mockCallApi.mock.calls[0][1].basicInfo.sceneInfo.advisorShare).toBe(sceneInfo.advisorShare);
|
|
57
|
-
}));
|
|
58
|
-
});
|
|
59
|
-
describe('OrderCoreService.acknowledgeAutoConfirmedOrder', () => {
|
|
60
|
-
beforeEach(() => {
|
|
61
|
-
mockCallApi.mockReset().mockResolvedValue(undefined);
|
|
62
|
-
});
|
|
63
|
-
it('只发送已知晓公开字段', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
64
|
-
const request = {
|
|
65
|
-
tradeOrderId: 'order-1',
|
|
66
|
-
operator: 'supplier-user',
|
|
67
|
-
};
|
|
68
|
-
yield service_1.default.acknowledgeAutoConfirmedOrder(request);
|
|
69
|
-
expect(mockCallApi).toHaveBeenCalledWith(expect.stringMatching(/\/core\/acknowledge-auto-confirmed-order$/), request);
|
|
70
|
-
expect(mockCallApi.mock.calls[0][1]).not.toHaveProperty('operatorRole');
|
|
71
|
-
}));
|
|
72
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|