From c54dbda3444e1ad7c768bd12687ed443b393c866 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 10 Oct 2026 20:10:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(douyin-mashida):=20=E5=85=A8=E9=9D=A2?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=B8=BA=20/h5/<=E8=AE=A2=E5=8D=95=E5=8F=B7>?= =?UTF-8?q?=20=E7=9B=B4=E5=85=85=E8=B7=AF=E5=BE=84=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=9D=A2=E6=9D=BF=E6=8F=90=E7=A4=BA=E4=B8=8E=E7=9B=B4?= =?UTF-8?q?=E5=8F=91=E9=93=BE=E6=8E=A5=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 抖音码速达买家直充链接与兜底配置基址统一改为 https://<域名>/h5(直充路径为 /h5/<订单号>) - 前端配置面板示例与提示升级为真实 16 位平台订单号形态(如 /h5/2628301923197355) - 一键填入域名同步适配 /h5 基址,并在后端发券与发货私信推送中直接生成 /h5/<订单号> - 数据库本地配置同步更新为 http://localhost/h5 验证方式:前端类型检查与打包构建通过,全量 132 项前端单测与 674 项后端单测全部通过 --- .../douyin-mashida/notify-service.ts | 35 ++++++++++++------- .../services/redeem/certificate-service.ts | 8 ++++- .../admin/platform/DouyinMashidaPanel.tsx | 22 ++++++------ apps/frontend/src/utils/douyin-mashida.ts | 10 +++--- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/apps/backend/src/services/platforms/douyin-mashida/notify-service.ts b/apps/backend/src/services/platforms/douyin-mashida/notify-service.ts index 8e950557..6f2cc3af 100644 --- a/apps/backend/src/services/platforms/douyin-mashida/notify-service.ts +++ b/apps/backend/src/services/platforms/douyin-mashida/notify-service.ts @@ -246,23 +246,34 @@ async function notifyBuyerClaimLink(input: { if (input.config.claimMessageEnabled === false) { return } - if (!String(input.config.claimLinkBase || '').trim()) { - return - } - - const claimLink = await resolveOrderItemClaimLink({ - orderId: input.order.id, - orderItemId: input.orderItem.id, - claimLinkBase: input.config.claimLinkBase, - }) - if (!claimLink.claimUrl) { + const claimLinkBase = String(input.config.claimLinkBase || '').trim().replace(/\/+$/, '') + if (!claimLinkBase) { return } const orderNo = resolveReceiptOrderId(input.order, input.orderItem) + let claimUrl = '' + let taskId: number | undefined + + if (claimLinkBase.endsWith('/h5') || claimLinkBase.includes('/h5/')) { + claimUrl = `${claimLinkBase}/${orderNo}` + } else { + const claimLink = await resolveOrderItemClaimLink({ + orderId: input.order.id, + orderItemId: input.orderItem.id, + claimLinkBase: input.config.claimLinkBase, + }) + claimUrl = claimLink.claimUrl + taskId = claimLink.taskId + } + + if (!claimUrl) { + return + } + const content = buildDouyinMashidaClaimMessage({ config: input.config, - claimUrl: claimLink.claimUrl, + claimUrl, sku: input.orderItem.sku_code, orderNo, productTitle: input.orderItem.sku_name, @@ -277,7 +288,7 @@ async function notifyBuyerClaimLink(input: { orderId: input.order.id, orderItemId: input.orderItem.id, orderNo, - taskId: claimLink.taskId, + taskId, }) } diff --git a/apps/backend/src/services/redeem/certificate-service.ts b/apps/backend/src/services/redeem/certificate-service.ts index 4fcd07d5..6d4362ab 100644 --- a/apps/backend/src/services/redeem/certificate-service.ts +++ b/apps/backend/src/services/redeem/certificate-service.ts @@ -140,10 +140,16 @@ export async function resolveDouyinMashidaCertLink(input: { config: DouyinMashidaSourceConfig }): Promise { const fallback = String(input.config.certLink || '').trim() - if (!String(input.config.claimLinkBase || '').trim()) { + const claimLinkBase = String(input.config.claimLinkBase || '').trim().replace(/\/+$/, '') + if (!claimLinkBase) { return fallback } + const orderNo = resolveReceiptOrderId(input.order, input.orderItem) + if (claimLinkBase.endsWith('/h5') || claimLinkBase.includes('/h5/')) { + return `${claimLinkBase}/${orderNo}` + } + const claimLink = await resolveOrderItemClaimLink({ orderId: input.order.id, orderItemId: input.orderItem.id, diff --git a/apps/frontend/src/pages/admin/platform/DouyinMashidaPanel.tsx b/apps/frontend/src/pages/admin/platform/DouyinMashidaPanel.tsx index 6f416bb7..39e63995 100644 --- a/apps/frontend/src/pages/admin/platform/DouyinMashidaPanel.tsx +++ b/apps/frontend/src/pages/admin/platform/DouyinMashidaPanel.tsx @@ -78,8 +78,8 @@ function createEmptyConfig(): AdminDouyinMashidaConfig { secret: '', secretMasked: '', hasSecret: false, - certLink: '', - claimLinkBase: '', + certLink: 'http://localhost/h5', + claimLinkBase: 'http://localhost/h5', messageTemplate: '您的订单已发货,请点击链接领取:{link}', claimMessageEnabled: true, afterVerifyLink: '', @@ -144,11 +144,11 @@ export function DouyinMashidaPanel() { const origin = window.location.origin setConfig((prev) => ({ ...prev, - claimLinkBase: `${origin}/claim`, - certLink: `${origin}/redeem`, - afterVerifyLink: `${origin}/redeem`, + claimLinkBase: `${origin}/h5`, + certLink: `${origin}/h5`, + afterVerifyLink: `${origin}/h5`, })) - showSuccess('已自动填入当前域名,请点击右上角「保存配置」以生效') + showSuccess('已自动填入当前域名(/h5/<订单号>),请点击右上角「保存配置」以生效') } /** 向平台订阅或停用推送 */ @@ -439,11 +439,11 @@ export function DouyinMashidaPanel() { - 买家领取页基址 (claimLinkBase) + 买家 H5 直充链接基址 (claimLinkBase) updateField('claimLinkBase', event.target.value)} - placeholder="https://你的公网域名/claim" + placeholder="https://你的公网域名/h5" /> {claimLinkPreview ? ( @@ -464,14 +464,14 @@ export function DouyinMashidaPanel() { - 通用兑换链接 (certLink) + 兜底领取链接 (certLink) updateField('certLink', event.target.value)} - placeholder="https://你的公网域名/redeem" + placeholder="https://你的公网域名/h5" /> - 通用兜底兑换链接(无商品映射或旧单卡券兜底) + 通用兜底链接,默认与直充 H5 基址保持一致 diff --git a/apps/frontend/src/utils/douyin-mashida.ts b/apps/frontend/src/utils/douyin-mashida.ts index 5b99e54a..39e8d5cb 100644 --- a/apps/frontend/src/utils/douyin-mashida.ts +++ b/apps/frontend/src/utils/douyin-mashida.ts @@ -64,9 +64,9 @@ export function describeMissingConfigFields(config: MissingConfigCheck): string[ } /** - * 面板预览用的示例 token(48 位 hex,与真实 claim token 形态一致)。 + * 面板预览用的示例订单号(与真实 16 位平台订单号形态一致)。 */ -const CLAIM_LINK_PREVIEW_TOKEN = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6' +const CLAIM_LINK_PREVIEW_TOKEN = '2628301923197355' /** * 拼一条领取链接预览:`claimLinkBase` 去尾斜杠 + '/' + token。 @@ -88,16 +88,16 @@ export function buildDouyinMashidaClaimLinkPreview( * 领取链接基址的误配提示(无问题返回空串)。 * * 重点拦 `#`:`#/claim/{token}` 是**打手**领取页(要绑游戏 UID), - * 皮肤买家链接必须是路径式,配错会把买家带到打手流程里。 + * 皮肤买家链接必须是路径式,例如 https://你的域名/h5 */ export function describeClaimLinkBaseWarning(claimLinkBase: string): string { const text = String(claimLinkBase ?? '').trim() if (!text) return '' if (text.includes('#')) { - return '不要带 #:#/claim 是打手领取页,皮肤买家链接请用路径式,例如 https://你的域名/claim' + return '不要带 #:#/claim 是打手领取页,买家直充链接请用路径式,例如 https://你的域名/h5' } if (!/^https?:\/\//i.test(text)) { - return '请填完整地址(http:// 或 https:// 开头),例如 https://你的域名/claim' + return '请填完整地址(http:// 或 https:// 开头),例如 https://你的域名/h5' } return '' }