diff --git a/apps/backend/data/kuaishou-cloud-fulfillment.json b/apps/backend/data/kuaishou-cloud-fulfillment.json index 341b2d2a..9cb6b09e 100644 --- a/apps/backend/data/kuaishou-cloud-fulfillment.json +++ b/apps/backend/data/kuaishou-cloud-fulfillment.json @@ -52,32 +52,6 @@ "kuaishouConsumeShopId": "4269276762", "kuaishouConsumeShopName": "稚嫩游戏交易店", "notes": "从 khhao 订单 2612400002308936 导入" - }, - { - "id": "套装-浪漫天命", - "enabled": false, - "priority": 100, - "provider": "khhao", - "platform": "kuaishou", - "shopId": "4269276762", - "khhaoShopId": "10", - "internalSkuCode": "套装-浪漫天命", - "internalSkuName": "测试1", - "externalSkuCode": "830", - "externalItemId": "830", - "externalSkuName": "测试1", - "resolvedSkuName": "测试1", - "cloudSourceKey": "default", - "cloudSkuId": 28, - "cloudSkuName": "套装-浪漫天命", - "vnKey": "1", - "autoBuyEnabled": true, - "minAssetReserve": 0, - "autoReturnNumberAfterDispatch": true, - "autoConsumeAfterDispatch": true, - "kuaishouConsumeShopId": "4269276762", - "kuaishouConsumeShopName": "稚嫩游戏交易店", - "notes": "从 khhao 订单 2612300173165561 导入" } ] } diff --git a/apps/backend/src/services/open-91/order-query-service.js b/apps/backend/src/services/open-91/order-query-service.js index 61ab2262..a0202494 100644 --- a/apps/backend/src/services/open-91/order-query-service.js +++ b/apps/backend/src/services/open-91/order-query-service.js @@ -142,15 +142,30 @@ export async function queryOpen91Order(payload = {}, { requestId = '' } = {}) { }) } - return buildOpen91SuccessResponse({ + const outTradeNo = buildOpen91OutTradeNo(order) + const encryptedCards = buildOpen91Cards(cardItems, config) + const responseData = { orderNo: normalized.orderNo, - outTradeNo: buildOpen91OutTradeNo(order), + outTradeNo, orderStatus: 20, failCode: 0, failReason: '', orderCost: buildOpen91OrderCost(0), - cards: buildOpen91Cards(cardItems, config), + cards: encryptedCards, + } + + logWebhook('[open-91/query]', '91卡券订单查询返回 cards 已生成', { + requestId, + orderNo: normalized.orderNo, + orderId: order.id, + outTradeNo, + cardsEncoding: config.cardsEncoding, + cardsPlain: cardItems, + cardsEncrypted: encryptedCards, + responseData, }) + + return buildOpen91SuccessResponse(responseData) } function resolveOpen91OrderFailReason(order) { diff --git a/apps/backend/src/services/open-91/shared.js b/apps/backend/src/services/open-91/shared.js index c17e8f2b..e3584619 100644 --- a/apps/backend/src/services/open-91/shared.js +++ b/apps/backend/src/services/open-91/shared.js @@ -246,11 +246,42 @@ export function buildOpen91CardItem({ claimUrl = '', expireTime = '' } = {}) { return { cardNo: normalizedClaimUrl, cardPwd: '', - expireTime: normalizeOpen91String(expireTime), + expireTime: formatOpen91ExpireTime(expireTime), jumpLink: normalizedClaimUrl, } } +/** @param {unknown} value */ +export function formatOpen91ExpireTime(value = '') { + const normalized = normalizeOpen91String(value) + if (!normalized) { + return '' + } + + if (/^\d{10}$/.test(normalized) || /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(normalized)) { + return normalized + } + + const date = value instanceof Date ? value : new Date(normalized) + if (Number.isNaN(date.getTime())) { + return normalized + } + + const parts = new Intl.DateTimeFormat('zh-CN', { + timeZone: 'Asia/Shanghai', + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hourCycle: 'h23', + }).formatToParts(date) + + const byType = Object.fromEntries(parts.map((part) => [part.type, part.value])) + return `${byType.year}-${byType.month}-${byType.day} ${byType.hour}:${byType.minute}:${byType.second}` +} + export function resolveOpen91QueryState({ tasks = [], readyTaskIds = [] } = {}) { const normalizedTasks = Array.isArray(tasks) ? tasks : [] const readyTaskIdSet = new Set((Array.isArray(readyTaskIds) ? readyTaskIds : []).map((item) => Number(item))) diff --git a/apps/backend/src/services/open-91/shared.test.js b/apps/backend/src/services/open-91/shared.test.js index e35df9a7..ddaba561 100644 --- a/apps/backend/src/services/open-91/shared.test.js +++ b/apps/backend/src/services/open-91/shared.test.js @@ -5,6 +5,7 @@ import { buildOpen91CardItem, buildOpen91SignSource, encryptOpen91Cards, + formatOpen91ExpireTime, resolveOpen91QueryState, signOpen91Payload, verifyOpen91Signature, @@ -123,3 +124,9 @@ test('buildOpen91CardItem mirrors claim link into jumpLink and leaves password e }, ) }) + +test('formatOpen91ExpireTime keeps 91-compatible values and normalizes dates to Beijing time', () => { + assert.equal(formatOpen91ExpireTime('2026-05-05 12:00:00'), '2026-05-05 12:00:00') + assert.equal(formatOpen91ExpireTime('1778674551'), '1778674551') + assert.equal(formatOpen91ExpireTime(new Date('2026-05-14T12:15:51.842Z')), '2026-05-14 20:15:51') +})