feat(trade): v4.0.13 收货禁全市与小飞侠拒单可感知
CI / verify (push) Waiting to run

拦截伪区县写入与下单;推单失败挂 fulfillmentHold,HQ 可见超区/推单失败。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-02 17:20:40 +08:00
parent 63fcf33416
commit c6b01def4c
18 changed files with 613 additions and 76 deletions
+3 -1
View File
@@ -7,7 +7,7 @@
| 维度 | 结论 |
|------|------|
| 版本线 | **v4.0.9** 合伙人 H5 周结算、预付款、子账号用户管理 |
| 版本线 | **v4.0.13** 收货地址禁「全市」+ 小飞侠拒单履约可感知 |
| 订单佣金 | 区县归属已删除;只认关联 / 代下单选择 |
| 账单 | 酒订单 / 核销订单分列;合伙人改为周账(周一 08:00);零元不同步合伙人;酒厂含现场提货,零应付仍出账(无需打款) |
| 活动图 | HQ 上传底图/码栏/文案;合伙人选择写入库;HQ 可指定一张图为勾选主合伙人合成下载;子账号不可看活动图 |
@@ -21,6 +21,7 @@
| 4.0.6 | [`酒厂对账核对`](./杜康好客-v4.0.6-开发文档.md) | ✅ 已实现 |
| 4.0.7 | [`活动图快链与勾选导出`](./杜康好客-v4.0.7-开发文档.md) | ✅ 已实现 |
| 4.0.9 | [`合伙人 H5 周结算与用户管理`](./杜康好客-v4.0.9-开发文档.md) | ✅ 已实现 |
| 4.0.13 | [`收货地址把关与拒单可感知`](./杜康好客-v4.0.13-开发文档.md) | ✅ 已实现 |
| 日期 | 说明 |
|------|------|
@@ -31,3 +32,4 @@
| 2026-08-31 | v4.0.6:酒厂 T+3=每 3 天出一期(非每日);含现场提货;零应付仍出账;核对补生成 |
| 2026-09-01 | v4.0.7HQ 城市合伙人活动图快链;单张/勾选导出合成图(PNG / zip) |
| 2026-09-02 | v4.0.9:子账号默认启用;关联码已扫码;零元账单不同步;银行账号自填;周账周一 08:00;预付款预估;子账号用户管理无活动图 |
| 2026-09-02 | v4.0.13:收货禁「全市」;脏地址下单拦截;小飞侠超区/推单失败挂 `fulfillmentHold`(不做仓/收件坐标) |
+93
View File
@@ -0,0 +1,93 @@
# 杜康好客 · v4.0.13 开发文档
> **2026-09-02** · mini-user / iam / trade / fulfillment / admin-web / h5-partner / domain
> **主题**:收货地址严格把关(禁「全市」)+ 小飞侠拒单履约可感知
需求背景:订单 `DK2026090220803` 自动推小飞侠失败,原因 `超出服务区`;收货区县为门店筛选用伪值「全市」。
**不做(本版明确排除)**:仓坐标补全、收件 `chooseLocation` / `toCoordinate` 坐标链路。
---
## 1. 版本目标
| # | 任务 | 类型 | 交付 |
|---|------|------|------|
| 1 | 收货选区与门店筛选语义拆分 | 缺陷/体验 | `RegionPicker mode=shipping` 无「全市」 |
| 2 | 地址保存硬闸 | 缺陷 | 前后端拒伪区县;详细地址最短 8 字 |
| 3 | 下单/预览拦截脏地址 | 缺陷 | preview `addressOk=false`;确认页不可提交 |
| 4 | 地址簿存量提示 | 体验 | 「需完善」置顶;结账选址强制去编辑 |
| 5 | 推单失败可感知 | 缺陷 | `fulfillmentHold` + 原因码;HQ 标签/发货弹窗 |
---
## 2. 规则
### 2.1 收货地址
- 伪区县:`全市``全部`、空 —— **禁止**写入 `user_address` / 下单快照 / 合伙人代下单。
- 详细地址:必填且长度 ≥ 8;含「全市」且无街道路牌关键词时前端软提示(不单独硬失败)。
- 门店列表筛选仍可使用「全市」(`mode=filter`)。
- 现场取货地址快照(`现场/现场/取货`)不走本校验。
### 2.2 履约拦截(推单失败)
| `fulfillment_hold_reason` | 含义 | HQ 展示 |
|---------------------------|------|---------|
| `LARGE_ORDER_GE_10_BOXES` | 大单≥10箱 | 大单 |
| `COURIER_OUT_OF_SERVICE` | 小飞侠返回超出服务区 | 超区 |
| `COURIER_DISPATCH_FAILED` | 其它自动推单失败 | 推单失败 |
- 失败后仍写 `MANUAL` 配送记录(待发货),**同时**挂 `fulfillmentHold`,避免静默像「没推过」。
- HQ 手动推小飞侠或填快递成功后仍清 hold(既有逻辑)。
---
## 3. 变更面
| 层 | 路径 |
|----|------|
| domain | `packages/domain/src/shipping-address.ts` |
| shared-types | `FULFILLMENT_HOLD_REASON_LABELS` + 常量 |
| API | `UserAddressService``TradeService` preview/create/代下单;`FulfillmentService.markCourierDispatchHold` |
| C 端 | `RegionPicker``address-edit``addresses``order-confirm``shipping-address.ts` |
| 合伙人 | `ProxyOrderPage` 校验 |
| HQ | `OrdersPage` 拦截筛选文案与标签 |
---
## 4. API 行为变化
| 接口 | 变化 |
|------|------|
| `POST/PUT /user/addresses` | 伪区县 / 详情过短 → `400` |
| `POST /trade/orders/preview` | 脏地址 → `addressOk=false` + message |
| `POST /trade/orders` | 脏地址 → `400` |
| 合伙人代下单 | 同上校验 |
| 支付后自动推单 | 失败写 hold reason(库字段,无新 path |
---
## 5. 验收
- [ ] 新增/编辑地址:区县列表无「全市」;选真实区县 + 足够详细地址可保存
- [ ] 保存「全市」或过短详情被拒(前端 toast + 后端 400
- [ ] 历史脏地址在地址簿标「需完善」;结账选择时跳转编辑
- [ ] 确认订单页脏地址不可提交;preview 提示完善区县
- [ ] 合伙人代下单伪区县/过短详情被拒
- [ ] 模拟小飞侠「超出服务区」后:订单 `fulfillment_hold=1``reason=COURIER_OUT_OF_SERVICE`HQ 列表「超区」、发货弹窗有说明
- [ ] 门店列表筛选「全市」行为不变
---
## 6. 存量建议(运维,非代码)
```sql
-- 生产排查脏地址(只读)
SELECT id, user_id, province, city, district, detail, updated_at
FROM user_address
WHERE district IN ('全市','全部','') OR district IS NULL
ORDER BY id DESC LIMIT 100;
```
可选:运营通知用户进「地址管理」完善;本版不自动改写历史行。