From 3dbb73e428bc952691cbcd88de092bed6a043e16 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Wed, 26 Aug 2026 17:24:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(trade):=20v3.5.14=20=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E8=AE=A2=E5=8D=95/=E6=94=AF=E4=BB=98=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E4=BD=BF=E7=94=A8=E7=9C=9F=E5=AE=9E=20client?= =?UTF-8?q?App?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- docs/杜康好客-v3-PRD.md | 1 + docs/杜康好客-v3-埋点规范.md | 1 + docs/杜康好客-v3-现状对照.md | 2 + docs/杜康好客-v3.5.14-开发文档.md | 95 +++++++++++++++++++ docs/杜康好客-v3编码手册.md | 2 + .../src/modules/trade/trade.controller.ts | 2 +- .../src/modules/trade/trade.service.ts | 46 +++++++-- 7 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 docs/杜康好客-v3.5.14-开发文档.md diff --git a/docs/杜康好客-v3-PRD.md b/docs/杜康好客-v3-PRD.md index 2af96a7..5dfd823 100644 --- a/docs/杜康好客-v3-PRD.md +++ b/docs/杜康好客-v3-PRD.md @@ -32,6 +32,7 @@ | 3.5.10 | 08-25 | C 端门店详情去掉分享按钮;同城送提示改承运商 HTML;小程序客服接通企微微信客服 | [`v3.5.10`](./杜康好客-v3.5.10-开发文档.md) | | 3.5.11 | 08-26 | 城市三种履约起购;企微通知补提交人/规格物流/核销用户;结算任务失败通知 + 提现通过/四账单 | [`v3.5.11`](./杜康好客-v3.5.11-开发文档.md) | | 3.5.12 | 08-26 | 发布会订单大屏循环 BGM;HQ 日志/订单状态流转/用户行为时间线展示中文;修复删除门店分类被默认树回种;HQ 侧栏顺序(业务前 11 项,系统设置置底) | [`v3.5.12`](./杜康好客-v3.5.12-开发文档.md) | +| 3.5.14 | 08-26 | 修复 C 端提交订单/支付成功日志误标 H5;线上 `order_submit`/`pay_success` 回填为小程序 | [`v3.5.14`](./杜康好客-v3.5.14-开发文档.md) | --- diff --git a/docs/杜康好客-v3-埋点规范.md b/docs/杜康好客-v3-埋点规范.md index 54ebd25..008a6e4 100644 --- a/docs/杜康好客-v3-埋点规范.md +++ b/docs/杜康好客-v3-埋点规范.md @@ -9,6 +9,7 @@ 2. 携带 `sessionId`(`dukang_session_id`) 3. `extraJson` 无密码/令牌;手机号脱敏 4. eventName 先登记 taxonomy 再 emit +5. C 端后端埋点 `clientApp` 取 JWT / `X-Client-App`(`USER_MINI` 或 `USER_H5`),禁止写死 H5。`order_submit` / `pay_success` 见 v3.5.14。 ## API diff --git a/docs/杜康好客-v3-现状对照.md b/docs/杜康好客-v3-现状对照.md index 7af701c..c1e4994 100644 --- a/docs/杜康好客-v3-现状对照.md +++ b/docs/杜康好客-v3-现状对照.md @@ -67,9 +67,11 @@ | 3.5.10 | [`门店详情去分享按钮 + 同城提示改承运商 HTML + 小程序客服接企微`](./杜康好客-v3.5.10-开发文档.md) | 🔶 开发完成 | | 3.5.11 | [`城市履约起购 + 企微通知字段与结算通知`](./杜康好客-v3.5.11-开发文档.md) | 🔶 开发完成 | | 3.5.12 | [`订单大屏 BGM + HQ 中文展示 + 修复删除门店分类 + HQ 侧栏顺序`](./杜康好客-v3.5.12-开发文档.md) | 🔶 开发完成 | +| 3.5.14 | [`提交订单/支付成功日志端回填 USER_MINI`](./杜康好客-v3.5.14-开发文档.md) | 🔶 开发完成 | | 日期 | 说明 | |------|------| +| 2026-08-26 | v3.5.14:`order_submit`/`pay_success` 埋点改用真实 `clientApp`;线上这两类 `USER_H5` 回填为 `USER_MINI` | | 2026-08-26 | v3.5.12:订单大屏循环 BGM;HQ 日志/订单状态流转/用户行为时间线英文码改中文;修复删除门店分类后被 `ensureDefaults` 回种;HQ 侧栏按业务前 11 项重排、系统设置置底 | | 2026-08-26 | v3.5.11:城市三种履约起购;企微补提交人/订单规格物流收货/核销用户;`alert.settlement` 改名「结算任务失败通知」归入系统监控;补提现通过与四账单 | | 2026-08-25 | v3.5.10:门店详情去掉分享按钮;同城送提示改承运商 HTML(`GET /catalog/local-deliveries`);小程序客服接通企微(需填 CorpID) | diff --git a/docs/杜康好客-v3.5.14-开发文档.md b/docs/杜康好客-v3.5.14-开发文档.md new file mode 100644 index 0000000..f25bd0b --- /dev/null +++ b/docs/杜康好客-v3.5.14-开发文档.md @@ -0,0 +1,95 @@ +# 杜康好客 · v3.5.14 修复文档 + +> **2026-08-26** · API / 生产数据 +> **主题**:C 端「提交订单 / 支付成功」用户日志 `client_app` 误标为 H5,改为真实端(小程序) + +--- + +## 1. 版本目标 + +1. 修复后端埋点:`order_submit`、`pay_success` 不再写死 `USER_H5`。 +2. 回填线上 `log_user_analytics`:这两类事件里所有 `USER_H5` 改为 `USER_MINI`。 + +**不做**:核销埋点(`benefit_redeem_*`);未登录 `POST /analytics/events` 的 H5 回退;H5 预览(`TARO_ENV=h5`)仍报 `USER_H5`。 + +--- + +## 2. 根因 + +`TradeService.createOrder` / `payOrder`(Mock 支付成功)/ `handlePaySuccess`(微信支付回调)调用 `analyticsService.trackOneSafe` 时把 `clientApp` 写死为 `'USER_H5'`。 + +当前生产 C 端是 `apps/mini-user` 小程序,请求头为 `X-Client-App: USER_MINI`(写入 JWT)。同链路的 `pay_fail` 已使用 JWT 的 `clientApp`,故 HQ「用户日志」里提交订单显示「C 端 H5」,支付失败却可能显示「C 端小程序」。 + +--- + +## 3. 代码规则 + +| 写入点 | 事件 | `clientApp` | +|--------|------|-------------| +| `POST /trade/orders` → `createOrder` | `order_submit` | JWT `user.clientApp`(缺省 `USER_H5`) | +| `POST /trade/orders/:id/pay` Mock 成功 | `pay_success` | 入参 `clientApp`(缺省 `USER_H5`) | +| 微信支付回调 `handlePaySuccess` | `pay_success` | 该订单已有 `order_submit` / `pay_fail`;没有则默认 `USER_MINI` | + +小程序原生:`USER_MINI`。`mini-user` H5 预览:仍为 `USER_H5`(公众号 JSAPI,见 `apps/mini-user/src/lib/api.ts`)。 + +--- + +## 4. 线上数据回填 + +表:`log_user_analytics` +范围:`event_name IN ('order_submit', 'pay_success') AND client_app = 'USER_H5'` +动作:`client_app` → `USER_MINI` + +```sql +SELECT event_name, client_app, COUNT(*) AS cnt +FROM log_user_analytics +WHERE event_name IN ('order_submit', 'pay_success') +GROUP BY event_name, client_app; + +UPDATE log_user_analytics +SET client_app = 'USER_MINI' +WHERE event_name IN ('order_submit', 'pay_success') + AND client_app = 'USER_H5'; +``` + +回填结果(生产,2026-08-26): + +| 事件 | 回填前 USER_H5 | 回填后 USER_MINI | +|------|----------------|------------------| +| `order_submit` | 55 | 55 | +| `pay_success` | 28 | 28 | + +共更新 **83** 行。回填后这两类事件已无 `USER_H5`。 + +不改其它事件名,不改 `common_event`。 + +--- + +## 5. 关键路径 + +| 域 | 路径 | +|----|------| +| 建单 / 支付埋点 | `server/dukang-api/src/modules/trade/trade.service.ts` | +| 建单入参 | `server/dukang-api/src/modules/trade/trade.controller.ts` | +| HQ 展示 | `order_submit` → 提交订单;`USER_MINI` → C 端小程序 | + +无 Prisma 迁移。 + +--- + +## 6. 发版步骤 + +| 步骤 | 动作 | +|------|------| +| 1 | 生产已执行上述 `UPDATE`(历史行) | +| 2 | 发 `dukang-api`(新单按 JWT 写端);`--skip-db` | + +--- + +## 7. 验收清单 + +- [x] 历史 `order_submit` / `pay_success` 在 HQ 不再显示「C 端 H5」(已回填 83 行) +- [ ] 小程序提交订单后,HQ 用户日志「提交订单」端为「C 端小程序」(需发 `dukang-api`) +- [ ] 小程序支付成功后,「支付成功」端为「C 端小程序」(需发 `dukang-api`) +- [ ] `mini-user` H5 预览下单仍为「C 端 H5」 +- [ ] 浏览类埋点(`home_view` 等)端字段不变 diff --git a/docs/杜康好客-v3编码手册.md b/docs/杜康好客-v3编码手册.md index e1f8855..5fb2a75 100644 --- a/docs/杜康好客-v3编码手册.md +++ b/docs/杜康好客-v3编码手册.md @@ -51,6 +51,8 @@ C 端购酒核销 · 门店扫码核销+打款 · 合伙人拓店履约 · WebAd **订单大屏(v3.5.12)**:循环 BGM;右上角「播放 BGM」开关(需点击才出声);成交撒花时压低背景音。HQ 日志 / 订单状态流转 / 用户行为时间线展示中文(存储码不变)。删除门店分类后列表不再自动补种默认树(「同步默认分类」才补)。HQ 侧栏:概览→用户→商品→订单→开城→门店→财务→好客权益→配送单→工单→发票,其余运营/工具项随后,**系统设置最后**。 +**用户日志端(v3.5.14)**:`order_submit` / `pay_success` 的 `clientApp` 取 JWT(小程序 `USER_MINI`);微信支付回调沿用该订单已有埋点,缺省小程序。禁止再写死 `USER_H5`。 + ## 5. 验收用例(必过) **主链路 15 项**:登录、4 SKU、起购、支付+权益、双通道核销、payout、关店不可见、拓店审核、配送完成、退款、T+1/T+30… diff --git a/server/dukang-api/src/modules/trade/trade.controller.ts b/server/dukang-api/src/modules/trade/trade.controller.ts index 6fc1a9a..b55bcd7 100644 --- a/server/dukang-api/src/modules/trade/trade.controller.ts +++ b/server/dukang-api/src/modules/trade/trade.controller.ts @@ -26,7 +26,7 @@ export class TradeController { @Post() create(@CurrentUser() user: AuthUser, @Body() body: Record, @Req() req: Request) { - return this.tradeService.createOrder(user.actorId, body as never, req); + return this.tradeService.createOrder(user.actorId, body as never, req, user.clientApp); } @Get() diff --git a/server/dukang-api/src/modules/trade/trade.service.ts b/server/dukang-api/src/modules/trade/trade.service.ts index 560bda6..95bdec8 100644 --- a/server/dukang-api/src/modules/trade/trade.service.ts +++ b/server/dukang-api/src/modules/trade/trade.service.ts @@ -210,6 +210,31 @@ export class TradeService { }; } + private resolveTrackedUserClientApp( + clientApp?: ClientApp | string | null, + fallback: ClientApp = ClientApp.USER_H5, + ): ClientApp { + if (clientApp === ClientApp.USER_MINI || clientApp === ClientApp.USER_H5) { + return clientApp; + } + return fallback; + } + + /** 支付回调无 JWT:沿用该订单已有埋点;没有则默认小程序(当前生产 C 端) */ + private async clientAppForPaidOrder(userId: bigint, orderId: bigint): Promise { + const row = await this.prisma.logUserAnalytics.findFirst({ + where: { + userId, + refType: 'ORDER', + refId: orderId, + eventName: { in: ['order_submit', 'pay_fail'] }, + }, + orderBy: { createdAt: 'desc' }, + select: { clientApp: true }, + }); + return this.resolveTrackedUserClientApp(row?.clientApp, ClientApp.USER_MINI); + } + async createOrder( userId: bigint, body: { @@ -221,6 +246,7 @@ export class TradeService { skuId?: string; }, req: Request, + clientApp?: ClientApp, ) { const preview = await this.preview(userId, body); if (preview.quantityOk === false) { @@ -351,7 +377,7 @@ export class TradeService { ); } - this.analyticsService.trackOneSafe(userId, 'USER_H5', { + this.analyticsService.trackOneSafe(userId, this.resolveTrackedUserClientApp(clientApp), { eventName: 'order_submit', refType: 'ORDER', refId: order.id, @@ -480,7 +506,7 @@ export class TradeService { userId, }); - this.analyticsService.trackOneSafe(userId, 'USER_H5', { + this.analyticsService.trackOneSafe(userId, this.resolveTrackedUserClientApp(clientApp), { eventName: 'pay_success', refType: 'ORDER', refId: order.id, @@ -653,12 +679,16 @@ export class TradeService { orderNo: order.orderNo, userId: order.userId, }); - this.analyticsService.trackOneSafe(order.userId, 'USER_H5', { - eventName: 'pay_success', - refType: 'ORDER', - refId: order.id, - extraJson: { orderId: order.id.toString(), mode: 'wechat_callback' }, - }); + this.analyticsService.trackOneSafe( + order.userId, + await this.clientAppForPaidOrder(order.userId, order.id), + { + eventName: 'pay_success', + refType: 'ORDER', + refId: order.id, + extraJson: { orderId: order.id.toString(), mode: 'wechat_callback' }, + }, + ); } return { orderId: order.id.toString(), alreadyPaid: false };