|
@@ -0,0 +1,103 @@
|
|
|
|
|
+# 汇安融「门店地区 + 按顾客 IP 挑店」交付说明(2026-10-08)
|
|
|
|
|
+
|
|
|
|
|
+需求原文(用户,指向《支付宝支付接口接入规范261008(1).docx》):
|
|
|
|
|
+「门店新增了地区相关的字段,需要接上,因为支付宝异地收款会有风控,我们的 checkout 也要做一个调整,
|
|
|
|
|
+在调起支付时要根据请求者 ip 自动选择对应地区的门店 id,这样才不会风控」
|
|
|
|
|
+
|
|
|
|
|
+设计裁定(本批开工前已与你确认,未改):IP 归属地取 ipwho.is(进程内缓存,失败⇒空);
|
|
|
|
|
+粒度 = 城市精确匹配、省作回退;匹配不上**拒收**(顾客语,不打上游、不落单);同城多店按 IP
|
|
|
|
|
+稳定散列挑一家;城市未匹配则该省随机一家。
|
|
|
|
|
+
|
|
|
|
|
+★ 这一批**没有写计划文件**(前几批是 `.claude/plan/<日期>-<主题>.md`),需求以 docx + 上述裁定
|
|
|
|
|
+为准,落地过程与读数都在 `logs/` 下的日志里 —— 这点与既有批次不同,先说明。
|
|
|
|
|
+
|
|
|
|
|
+## 一、提交清单(5 个,全部**未 push**)
|
|
|
|
|
+
|
|
|
|
|
+`origin/java` 停在 `ed24480f`;本地 `java` 领先 5 个提交:
|
|
|
|
|
+
|
|
|
|
|
+| 提交 | 内容 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `b1c8f6fb` | (一)目录快照 `har-region-cn.json`(31 省 / 342 地级 / 166 繁简对)+ `HarRegionCatalog` + `HarIpRegionResolver` + `HarShopService#pickForRegion` + 门店/创建请求带地区 |
|
|
|
|
|
+| `32384b2b` | (二)结账腿接线:`HarCheckoutController` 按顾客 IP 挑店填 `third_shop_code`;挑不出来 400 拒收(两种文案);`clientIp` 四个头 + unknown 兜底 |
|
|
|
|
|
+| `c47b0c63` | (三)省市目录只读端点 `GET /payment/har/region/provinces|cities` + `db/har-shop-region.sql` |
|
|
|
|
|
+| `8e352e7e` | (四)前端:门店表单省→市选择器、列表「地区」列、详情一行;开放 API 文档 §10.1 同步 |
|
|
|
|
|
+| `ccc34414` | 文档 markdown 空行修正(引用块贴着 `---` 会被当成懒惰续行) |
|
|
|
|
|
+
|
|
|
|
|
+★ **`b1c8f6fb` 里夹带了一份 `支付宝支付接口接入规范261008(1).docx`(87512 字节)** ——
|
|
|
|
|
+那是开工时会话开始前就已经 `git add` 进索引的文件,提交时没有指定路径,于是跟着这一刀进去了。
|
|
|
|
|
+**我没有改写历史**(改写会动到你已经能看到的提交),要不要摘出来请你定:留着无功能影响,
|
|
|
|
|
+摘掉需要 rebase/改写。后续提交都改成「显式路径 `git add` + `git commit -F <msg> -- <显式路径>`」。
|
|
|
|
|
+
|
|
|
|
|
+## 二、验证读数(本机实测,不是推断)
|
|
|
|
|
+
|
|
|
|
|
+- 整包 `mvn test`:**1212 条,0 失败 0 错误 0 跳过,BUILD SUCCESS**(12:44:08;
|
|
|
|
|
+ `HarCheckoutControllerTest` 24 + `HarRegionControllerTest` 5 都在内)
|
|
|
|
|
+- 两向变异(每条都先确认变异体生效,还原后 sha256 复核回基线):
|
|
|
|
|
+
|
|
|
|
|
+| 变异体 | 结果 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| 结账腿:4 个(挑到不填 `third_shop_code` / 两句拒收文案合并 / 不取 XFF 第一跳 / 去掉 `unknown` 兜底) | 预测红集 **6**,实测恰为那 6 条,其余 18 条作正对照仍绿;还原后 sha256 与变异前一致(24/0) |
|
|
|
|
|
+| 目录端点:3 个(省码不校验 / 用空表代替 400 / 权限串只留 `:create`) | 预测红集 **3**,实测 `cities_returnsOnlyThatProvincesCities`、`cities_unknownProvinceCode_400_notAnEmptyList`、`contract_pathAndPermissionAreTheOnesTheFrontendCalls`;2 条对照仍绿;还原后 sha256 一致(5/0) |
|
|
|
|
|
+
|
|
|
|
|
+- 前端(**本仓无测试框架**,`ts:check` 基线本就非零 ⇒ 只能同机差分):
|
|
|
|
|
+ - `pnpm ts:check`:基线 **289** 条 error TS(exit 2)→ 改动后 **289** 条,**逐条集合比对
|
|
|
|
|
+ 新增 0、消失 0**;
|
|
|
|
|
+ - `pnpm build`:exit 0(两次,12:42 与 12:46,各约 57s);已回读产物确认新代码真进了包:
|
|
|
|
|
+ `dist/js/index.x_9MozyF.js` 含 `region/provinces`、`dist/js/index.SNmFzXAj.js` 含文档新句
|
|
|
|
|
+ 「城市编码不属于该省」、`province_code` 出现在 3 个 chunk(**注意**:一开始我按 `dist/assets/*.js`
|
|
|
|
|
+ 搜,3 个都查成 0 —— 是本仓产物在 `dist/js/`,是**检索式错**不是文件缺失,换路径后同一条搜索命中);
|
|
|
|
|
+ - Element Plus 用法对着**装好的包**核过(2.14.0 `select.mjs`):`loading: Boolean`
|
|
|
|
|
+ 「whether Select is loading data from server」、`disabled: Boolean` —— 不是凭记忆。
|
|
|
|
|
+
|
|
|
|
|
+- 产物指纹:
|
|
|
|
|
+ - `java/target/payment-platform-1.0.0.jar`,143,146,829 字节,**sha256 `977da28010f737bdde382af5a34b5a46a0967e607a62573231e5867ef0d0f4f9`**,
|
|
|
|
|
+ 重打于 12:45:02;已开包确认含 `HarRegionController.class`、`HarRegionVO.class`,
|
|
|
|
|
+ 且包内 `har-region-cn.json` 与源码 sha256 一致(`23c713b1…`)
|
|
|
|
|
+ - `frontend/dist.zip`,9,375,221 字节,**sha256 `dd4cf524bc11bc32d4a4e831793035b2ba2ca53b6ed0f957682c43708750dab1`**,
|
|
|
|
|
+ 281 个条目(与上一版同形:`dist/` 开头、**全为正斜杠**、`testzip()` 无坏成员)
|
|
|
|
|
+
|
|
|
|
|
+## 三、部署要求(按顺序,第 1 步不做就没有后面)
|
|
|
|
|
+
|
|
|
|
|
+1. **先执行 `java/src/main/resources/db/har-shop-region.sql`**(本工程未接 Flyway,只能你执行)。
|
|
|
|
|
+ ★ 反了不是静默降级而是**直接报错**:实体带上 `province_code`/`city_code` 后,门店列表与
|
|
|
|
|
+ 挑店的每一句 SELECT 都会取这两列 ⇒ 门店列表 500,且**每一笔收款链接下单都失败**。
|
|
|
|
|
+2. **重新部署**:上面的 jar + `frontend/dist.zip`。
|
|
|
|
|
+3. **不需要新菜单 SQL、不需要重登**:目录端点复用门店既有的
|
|
|
|
|
+ `module_payment:har:shop:list` / `:create` 权限串(`@perm.hasAny` 两条),菜单不动。
|
|
|
|
|
+4. **跑脚本末尾那三条只读核对**,尤其第 ③ 条 —— 它给出的数字就是**要重开的店数**:
|
|
|
|
|
+ `province_code`/`city_code` 为空的门店(2026-10-08 之前建的)在挑店时必然落选。
|
|
|
|
|
+ 上游没有门店更新接口,**补不上**,要收款只能换新的 `third_shop_id` 重开。
|
|
|
|
|
+5. 验收路径:门店管理 → 创建门店 → 省/市两级下拉能出目录 → 提交 → 列表「地区」列显示
|
|
|
|
|
+ 「江苏省 / 南京市」→ 从**另一个地区**的网络打开该商户的收款链接 → 应当拒收
|
|
|
|
|
+ (「无法确定您当前所在地区…」或「收款方暂未开通您所在地区的收款…」),而不是跑到异地门店。
|
|
|
|
|
+
|
|
|
|
|
+## 四、未验项(如实列,别当已验)
|
|
|
|
|
+
|
|
|
|
|
+1. **前端页面没有执行级验证**:本仓前端没有测试框架、也没有能起真 SFC 的台子,页面还要登录态
|
|
|
|
|
+ 才能打开。「选择器真的拉到 31 个省」「换省真的换了市」「列表把码显示成了名」这几步
|
|
|
|
|
+ **一次都没被真跑过** —— 我只能给到「类型零新增 + 构建通过 + 产物里确有这段代码」。
|
|
|
|
|
+2. **`pay_url` 仍然没有被真机扫过**(上一批就挂着的问题,本批没变);挑店在**真实顾客 IP**
|
|
|
|
|
+ 下的表现(真挑到哪个地区的哪家店)没有端到端跑过,只跑到「挑店逻辑 + 端口」这一层。
|
|
|
|
|
+3. **上游是否真的按地区风控**未知:本批保证的是「我们把对的码发上去、按地区挑店」,
|
|
|
|
|
+ 上游收了这两个码之后怎么用,没有可观测的读数。
|
|
|
|
|
+4. **X-Forwarded-For 可伪造**:除非前置代理覆写它(写在 `HarCheckoutController` 的类 javadoc
|
|
|
|
|
+ 「已知的边界」里)。后果是把顾客导到收款方自己的**另一家门店**,不涉资金与租户边界,
|
|
|
|
|
+ 要堵得在网关上 —— 本批不动。
|
|
|
|
|
+5. **`HarCheckoutController` 三个偏差未改**(都在代码注释里标了,属规范与现状不一致,
|
|
|
|
|
+ 不是本次引入):`shopName` 我方 `@Size(128)` vs 规范 varchar(16);`thirdShopCode` 32 vs 30;
|
|
|
|
|
+ 实体列宽按规范 16 建。
|
|
|
|
|
+
|
|
|
|
|
+## 五、要你知悉 / 拍板的两件
|
|
|
|
|
+
|
|
|
|
|
+1. **同类错误码两处不一致**(本次实测发现):编码不认识时,
|
|
|
|
|
+ `HarRegionController` 返回 `code=400`(我写的,两参构造),
|
|
|
|
|
+ 而 `HarShopService#requireKnownCity` 返回 **`code=500`**(单参 `BusinessException` 的默认码)。
|
|
|
|
|
+ 后者是对外开放 API 也会走到的路径 ⇒ 外部调用方传错码会看到「服务器错误」。**我没有改它**
|
|
|
|
|
+ (改的是既有端点契约 + 要动它的用例),只把开放文档按**实测**口径写成 500。要不要统一成 400,
|
|
|
|
|
+ 请你定。
|
|
|
|
|
+2. **`third_shop_id` 重复被判 `code=500`** 是文档里的既有说法,同上一类,本次未动。
|
|
|
|
|
+
|
|
|
|
|
+另:`probe` 结论提醒 —— 顾客端拒收文案能上屏,是因为 `GlobalExceptionHandler` 对
|
|
|
|
|
+`BusinessException` **不带 `@ResponseStatus`**(HTTP 200 + body `code`),顾客页自建实例
|
|
|
|
|
+读的是 `res.data.msg`;如果哪天有人把它改成真 4xx/5xx,顾客页会 fallback 成
|
|
|
|
|
+「网络异常,请检查网络后重试」,那句「你所在地区未开通」就再也到不了顾客眼前。
|