Files
dukang/docs/杜康好客-v3.5.1-开发文档.md
T
2026-08-19 16:20:33 +08:00

9.1 KiB
Raw Blame History

杜康好客 · v3.5.1 版本更新

2026-08-19 · admin-web / h5-shop / mini-user / h5-partner / API 工单方案(探索与拆分):杜康好客-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-dbuser_invoice_titlestore_info_change_requestuser_order@@index([status, payExpireAt])


1. 发布会订单大屏

  • 路由:/orders/big-screen不走 AdminLayout(无侧栏)。
  • 数据:GET /admin/orders/big-screen?limit=(上限 2000)。过滤 isTest=falsepayAmount >= 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_FIELDSpackages/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/ShanghaiOrderExpiryScheduler
  • 条件:PENDING_PAY + UNPAID + payExpireAt < now + isTest=false
  • 动作:条件更新 CANCELLED + cancelledAt;日志 operator=SYSTEM_AUTO_EXPIRE,备注「30 分钟未支付自动取消」
  • 不回滚权益券(与手动取消代下单一致;建单时未占用库存)
  • 每批最多 200 条;updateManystatus=PENDING_PAY 防并发重复
位置 说明
server/dukang-api/src/jobs/order-expiry.scheduler.ts 定时任务
TradeService.cancelExpiredPendingOrders 业务取消
schema.prisma Order @@index([status, payExpireAt])

9. 门店 / 合伙人接口强制退登录

登录后账号被停用 / 解绑时,后续任意鉴权接口拦截,前端清 token 并回登录页。

校验(仅 STORE / PARTNERUSER / HQ 跳过):

条件
门店 StoreAccount.status === ACTIVE;已选店则 Store.status === OPEN
合伙人 PartnerAccount.status === ACTIVEbindingStatus === 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_DISABLEDclearAuth + /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.ts3.5.1(当前仓库仍为 3.4.15
  • HQ 开发计划创建版本 v3.5.1 并关联本迭代任务