# 杜康好客 · v3.5.1 版本更新 > **2026-08-19** · admin-web / h5-shop / mini-user / h5-partner / API > 工单方案(探索与拆分):[`杜康好客-v3.5.1-工单迭代开发文档.md`](./杜康好客-v3.5.1-工单迭代开发文档.md) > 目标:发布会大屏、C 端发票抬头与申请、门店信息变更审核、待支付自动取消、门店/合伙人运行时强退登录。 ## 范围 | # | 项 | 交付 | |---|----|------| | 1 | 发布会订单大屏 | `GET /admin/orders/big-screen`;独立全屏页 `/orders/big-screen`;订单页「大屏」入口;超管「测试」连播假单 | | 2 | 门店端核销即时刷新 | `shop:redeem-success` 事件总线;Mine / 提现页监听后立刻重拉 | | 3 | C 端发票抬头 | 表 `user_invoice_title` + CRUD;我的 → 发票管理;图标操作(编辑 / 默认 / 删除) | | 4 | C 端申请发票 | 仅已完成订单;列表/详情「申请发票」与「开票中」互斥 | | 5 | 合伙人门店「提交变更」 | 表 `store_info_change_request`;审核通过才覆盖线上门店 | | 6 | 审核通知 | 原「套餐审核」菜单改为「审核通知」;套餐变更 + 信息变更双 Tab;badge 合计 PENDING | | 7 | 门店列表快捷入口 | `pendingInfoChangeId`;「审核信息 / 对比」;「提现」进结算页 | | 8 | 待支付 30 分钟自动取消 | Cron 每分钟;`payExpireAt` 已过期且非测试单 → `CANCELLED` | | 9 | 门店 / 合伙人运行时强退 | `JwtAuthGuard` 校验启停;`reason: ACCOUNT_DISABLED`;两端清登录并跳登录页 | **Prisma 迁移(发版不可 `--skip-db`)**:`user_invoice_title`、`store_info_change_request`;`user_order` 增 `@@index([status, payExpireAt])`。 --- ## 1. 发布会订单大屏 - 路由:`/orders/big-screen`,**不走** AdminLayout(无侧栏)。 - 数据:`GET /admin/orders/big-screen?limit=`(上限 2000)。过滤 `isTest=false` 且 `payAmount >= 100`;手机号 `maskContactPhone` 中间四位 `****`。 - 展示:一屏锁死、订单上滚;新成交按金额档位动效(首次进入不撒花)。 - 入口:订单监控「大屏」;超管另有「测试」,经 `BroadcastChannel` / `localStorage` 连播三级假单。 | 位置 | 说明 | |------|------| | `GET /admin/orders/big-screen` | `admin-orders.controller.ts`(须写在 `:id` 之前) | | `apps/admin-web/src/pages/BigScreenPage.tsx` | 大屏页 | | `apps/admin-web/src/lib/admin-events.ts` | 测试成交 payload | | `apps/admin-web/src/pages/OrdersPage.tsx` | 「大屏」「测试」按钮 | --- ## 2. 门店端核销即时刷新 核销成功页 `onMounted` 派发 `shop:redeem-success`;「我的」与「申请提现」监听后重拉接口,同 Tab 立即看到余额变化。 | 位置 | 说明 | |------|------| | `apps/h5-shop/src/lib/useRedeemSuccessBus.ts` | `notifyRedeemSuccess` / `useRedeemSuccessListener` | | `RedeemSuccessPage` / `MinePage` / `WithdrawPage` | 广播与刷新 | --- ## 3 / 4. C 端发票抬头与申请发票 **仅 `COMPLETED` 且非售后补发单**可申请。已有 `PENDING` / `ISSUED` 发票则不可再申(驳回后可重申)。 ### 抬头 - 我的 → **发票管理** → `/pages/invoice-titles/index` - 卡片操作:`编辑.png` / `默认.png` / `删除.png`(无文字按钮) - 新增/编辑走底部弹层(无独立编辑页) - 小程序标题只用原生 `navigationBarTitleText`,不再画二级 title(见 `.cursor/rules/mini-user-weapp-nav-title.mdc`) ### 申请 - 入口:订单列表卡片、订单详情底栏 - **互斥**:无进行中/已开具发票 →「申请发票」;状态 `PENDING` →「开票中」(点进申请页看进度);`ISSUED` 两者都不显示 - 列表接口带 `invoiceStatus`(最新一条发票) | 接口 | 说明 | |------|------| | `GET/POST /trade/invoice-titles` | 列表 / 新建 | | `PUT/DELETE /trade/invoice-titles/:id` | 编辑 / 删除(已被发票引用则 409) | | `GET /trade/orders/:orderId/invoice-titles` | 申请页可选抬头 | | `GET /trade/orders/:orderId/invoice-status` | `{ exists, status }` | | `POST /trade/orders/:id/invoices` | 提交申请(可传 `titleId`) | | `GET /trade/orders` | 列表项增加 `invoiceStatus` | | 前端 | 说明 | |------|------| | `apps/mini-user/src/pages/invoice-titles/index.tsx` | 抬头列表 | | `apps/mini-user/src/pages/invoice-apply/index.tsx` | 申请发票 | | `apps/mini-user/src/pages/orders/index.tsx` | 申请 / 开票中互斥 | | `apps/mini-user/src/pages/order-detail/index.tsx` | 同上 | --- ## 5 / 6 / 7. 门店信息变更与审核通知 合伙人改基本资料不再直接写 `store`,改为提交变更;审核通过后用 `proposedSnapshot` 覆盖线上。审核期间 C 端 / 合伙人端仍展示线上 live 数据。 可变字段白名单见 `STORE_INFO_CHANGEABLE_FIELDS`(`packages/shared-types/src/store-info-change.ts`)。 - 合伙人详情底栏文案:**提交变更**;有 PENDING 时黄条「基础信息变更审核中」 - 总部「套餐审核」改名 **审核通知**:Tab「套餐变更 | 门店信息变更」;菜单 badge = 两类 PENDING 之和 - 门店列表:`pendingInfoChangeId` →「审核信息 / 对比」;「提现」→ `/finance/store-bills?storeId=` - 门店端提交接口已备(`POST /shop/store/info-change-request`),**本期无门店 H5 入口** | 接口 | 角色 | 说明 | |------|------|------| | `POST /partner/stores/:storeId/info-change-request` | 合伙人 | 提交 | | `GET /partner/stores/:storeId/info-change-requests` | 合伙人 | 历史(用于 PENDING banner) | | `POST /shop/store/info-change-request` | 门店 | 已实现,本期无 UI | | `GET /admin/store-info-change-requests` | 总部 | 列表 | | `GET /admin/store-info-change-requests/summary` | 总部 | `pendingCount` | | `GET /admin/store-info-change-requests/:id` | 总部 | 详情 + 字段 diff | | `PUT /admin/store-info-change-requests/:id/audit` | 总部 | `APPROVE` / `REJECT` | | `GET /admin/stores` | 总部 | 增加 `pendingInfoChangeId` | 通过 / 驳回后派发既有 `admin:package-audit-changed` / `AUDIT_NOTICE_CHANGED_EVENT`,侧栏 badge 刷新。 --- ## 8. 待支付 30 分钟自动取消 下单路径已写 `payExpireAt = now + 30min`。本次补定时翻状态(此前 monitor 只告警不取消)。 - Cron:`*/1 * * * *` Asia/Shanghai,`OrderExpiryScheduler` - 条件:`PENDING_PAY` + `UNPAID` + `payExpireAt < now` + `isTest=false` - 动作:条件更新 `CANCELLED` + `cancelledAt`;日志 `operator=SYSTEM_AUTO_EXPIRE`,备注「30 分钟未支付自动取消」 - **不回滚**权益券(与手动取消代下单一致;建单时未占用库存) - 每批最多 200 条;`updateMany` 带 `status=PENDING_PAY` 防并发重复 | 位置 | 说明 | |------|------| | `server/dukang-api/src/jobs/order-expiry.scheduler.ts` | 定时任务 | | `TradeService.cancelExpiredPendingOrders` | 业务取消 | | `schema.prisma` Order | `@@index([status, payExpireAt])` | --- ## 9. 门店 / 合伙人接口强制退登录 登录后账号被停用 / 解绑时,后续任意鉴权接口拦截,前端清 token 并回登录页。 校验(仅 `STORE` / `PARTNER`;`USER` / `HQ` 跳过): | 端 | 条件 | |----|------| | 门店 | `StoreAccount.status === ACTIVE`;已选店则 `Store.status === OPEN` | | 合伙人 | `PartnerAccount.status === ACTIVE` 且 `bindingStatus === ACTIVE` | `isTest=true` 的账号 / 门店**跳过**,避免测试环境自锁。 响应:HTTP 403,`{ code, message, reason: 'ACCOUNT_DISABLED' }`(`HttpExceptionFilter` 透传 `reason`,避免走 401 refresh)。 | 位置 | 说明 | |------|------| | `jwt-auth.guard.ts` `assertAccountActive` | 运行时校验 | | `http-exception.filter.ts` | 透传 `reason` | | `apps/h5-shop/src/lib/api.ts` | `ACCOUNT_DISABLED` → `clearAuth` + `/login` | | `apps/h5-partner/src/lib/api.ts` | 同上(`toAppPath('/login')`) | --- ## 验收 - [ ] 大屏:非测试且实付 ≥100 的订单上屏,手机号中间四位 `****`;「测试」仅超管可见且能连播动效 - [ ] 门店端核销成功后,「我的 / 提现」无需下拉即可看到余额变化 - [ ] 发票管理可增删改、设默认;小程序无二级导航标题 - [ ] 仅已完成订单出现「申请发票」;提交后卡片改为「开票中」,不再出现申请按钮 - [ ] 合伙人提交门店资料后线上不立即变;总部审核通过后覆盖;驳回后可改再提 - [ ] 审核通知 badge = 套餐 PENDING + 信息变更 PENDING;门店列表有「审核信息 / 对比 / 提现」 - [ ] 构造已过期待支付正式单,约 1 分钟内变为已取消,且有 `SYSTEM_AUTO_EXPIRE` 日志;测试单不被取消 - [ ] 停用门店账号或关闭门店后,门店端接口 `reason=ACCOUNT_DISABLED` 并退登录;合伙人 `bindingStatus` 非 ACTIVE 同理;C 端不受影响 - [ ] `pnpm db:generate && npx prisma db push` 无报错;`pnpm lint` 无新增错误 ## 发版注意 - 必须跑 Prisma(两张新表 + 订单索引),不可 `--skip-db` - 发版时 mini-user `package.json` / `src/lib/client-version.ts` 升 **`3.5.1`**(当前仓库仍为 `3.4.15`) - HQ 开发计划创建版本 `v3.5.1` 并关联本迭代任务