Преглед на файлове

fix(汇安融): 省市编码订正为 GB/T 2260 完整六位 —— 对齐汇安融对照表

旧目录用的是数据集短码(省 32、市 3201),汇安融对照表(2026-10-09 截图)是
完整六位:省 2 位 + 0000、地级 4 位 + 00。编码是挑店的精确比对键,形状不对
门店会静默落选。

- har-region-cn.json 重生成:31 省 / 342 市 / 166 繁简对不变,只扩位;
  生成器(logs/,不进库)加扩位自检与 source.codes 注
- HarRegionCatalog:记录「数据集短码 → 输出六位」这条派生规则
- 8 个后端 javadoc、前端 har.ts / har-openapi.md / shop/index.vue 同步六位
- db/har-shop-region.sql:列注释订正 + 新增幂等短码订正 UPDATE(HAR 未上线,
  只影响测试环境已建的店)+ 只读核对 ④
- 7 个 HAR 测试类改钉六位形状与字面量;「旧短码必须查不到」保留为负例
alphaH преди 1 седмица
родител
ревизия
680406f9d7
променени са 20 файла, в които са добавени 942 реда и са изтрити 918 реда
  1. 4 4
      frontend/src/api/module_payment/har.ts
  2. 7 7
      frontend/src/views/module_payment/apikey/docs/har-openapi.md
  3. 2 2
      frontend/src/views/module_payment/har/shop/index.vue
  4. 2 2
      java/src/main/java/com/payment/platform/module/payment/har/client/dto/HarShopCreateRequest.java
  5. 1 1
      java/src/main/java/com/payment/platform/module/payment/har/controller/HarRegionController.java
  6. 1 1
      java/src/main/java/com/payment/platform/module/payment/har/dto/HarOrderCreateDTO.java
  7. 1 1
      java/src/main/java/com/payment/platform/module/payment/har/dto/HarRegionVO.java
  8. 2 2
      java/src/main/java/com/payment/platform/module/payment/har/dto/HarShopCreateDTO.java
  9. 2 2
      java/src/main/java/com/payment/platform/module/payment/har/dto/HarShopVO.java
  10. 2 2
      java/src/main/java/com/payment/platform/module/payment/har/entity/HarShopEntity.java
  11. 6 5
      java/src/main/java/com/payment/platform/module/payment/har/util/HarRegionCatalog.java
  12. 19 2
      java/src/main/resources/db/har-shop-region.sql
  13. 717 716
      java/src/main/resources/har-region-cn.json
  14. 9 9
      java/src/test/java/com/payment/platform/module/payment/har/controller/HarCheckoutControllerTest.java
  15. 16 16
      java/src/test/java/com/payment/platform/module/payment/har/controller/HarRegionControllerTest.java
  16. 4 4
      java/src/test/java/com/payment/platform/module/payment/har/dto/HarOrderCreateJsonContractTest.java
  17. 9 9
      java/src/test/java/com/payment/platform/module/payment/har/service/HarOrderServiceTest.java
  18. 78 78
      java/src/test/java/com/payment/platform/module/payment/har/service/HarShopRegionPickTest.java
  19. 11 10
      java/src/test/java/com/payment/platform/module/payment/har/service/HarShopServiceTest.java
  20. 49 45
      java/src/test/java/com/payment/platform/module/payment/har/util/HarRegionCatalogTest.java

+ 4 - 4
frontend/src/api/module_payment/har.ts

@@ -78,14 +78,14 @@ export interface HarShopVO {
   third_shop_id: string;
   shop_name?: string;
   /**
-   * GB/T 2260 两位省码(`32`=江苏省)。
+   * GB/T 2260 六位省码(`320000`=江苏省)。
    *
    * ★ 可空**只对 2026-10-08 之前建的门店**成立:上游没有门店更新接口,那批店补不上,
    *   挑店时会被当成「地区不匹配」落选 ⇒ 页面必须把「未填」显示成未填,
    *   不能看着像有地区(见 shop/index.vue 的 regionLabel)。
    */
   province_code?: string | null;
-  /** GB/T 2260 四位市码(`3201`=南京市),见 province_code */
+  /** GB/T 2260 六位市码(`320100`=南京市),见 province_code */
   city_code?: string | null;
   /** 上游返回的平台门店编号,创建成功才有值 */
   shop_id?: string;
@@ -128,9 +128,9 @@ export interface HarShopCreateParams {
  *   不要在前端另存省市表,也不要对码做加工(补零、截断、拼接)。
  */
 export interface HarRegionVO {
-  /** GB/T 2260:省两位(`32`),地级四位(`3201`) */
+  /** GB/T 2260 完整六位:省 `320000`,地级 `320100` */
   code: string;
-  /** 展示名。直辖市的地级是占位名换成的省名(重庆给出 5001/5002 两个) */
+  /** 展示名。直辖市的地级是占位名换成的省名(重庆给出 500100/500200 两个) */
   name: string;
 }
 

+ 7 - 7
frontend/src/views/module_payment/apikey/docs/har-openapi.md

@@ -49,10 +49,10 @@ POST /payment/openapi/alipay/shop/create
 | `merchant_code` | string | 否 | **你租户下配了多个商户号时必填**:门店建在哪一个商户下,见本章开头。只配了一个的不用传 |
 | `third_shop_id` | string | 是 | 你方的门店编码,≤32 字符。**重复提交会被拒绝**,见下 |
 | `shop_name` | string | 是 | 门店名称,≤128 字符 |
-| `province_code` | string | **是**(2026-10-08 起) | 门店所在省,GB/T 2260 两位码,如 `32`(江苏省)、`11`(北京市) |
-| `city_code` | string | **是**(2026-10-08 起) | 门店所在市,GB/T 2260 四位码,如 `3201`(南京市)、`1101`(北京市)。**必须是 `province_code` 下的市**,编码填错一律 `code=400` 拒绝(`msg` 形如 `城市编码不属于该省: 1101(省 32)`;省码写错则为 `省份编码不存在: 99`)—— 是调用方的输入错误,改对再调即可 |
+| `province_code` | string | **是**(2026-10-08 起) | 门店所在省,GB/T 2260 六位码,如 `320000`(江苏省)、`110000`(北京市) |
+| `city_code` | string | **是**(2026-10-08 起) | 门店所在市,GB/T 2260 六位码,如 `320100`(南京市)、`110100`(北京市)。**必须是 `province_code` 下的市**,编码填错一律 `code=400` 拒绝(`msg` 形如 `城市编码不属于该省: 110100(省 320000)`;省码写错则为 `省份编码不存在: 999999`)—— 是调用方的输入错误,改对再调即可 |
 
-> ⚠️ **`province_code` / `city_code` 自 2026-10-08 起为必填。** 在此之前提交成功的门店不传这两个字段也能建号,**现在不传会返回 `code=400`**(`msg` 为 `省份不能为空` / `城市不能为空`)。取值用国标 GB/T 2260 的行政区划代码:省级两位、地级四位,直辖市取 `1101`(北京市)/ `1201`(天津市)/ `3101`(上海市)/ `5001`(重庆市)、`5002`(重庆市县)。
+> ⚠️ **`province_code` / `city_code` 自 2026-10-08 起为必填。** 在此之前提交成功的门店不传这两个字段也能建号,**现在不传会返回 `code=400`**(`msg` 为 `省份不能为空` / `城市不能为空`)。取值用国标 GB/T 2260 的行政区划代码完整六位:省级 `XX0000`(2 位省码 + `0000`)、地级 `XXXX00`(4 位市码 + `00`),直辖市取 `110100`(北京市)/ `120100`(天津市)/ `310100`(上海市)/ `500100`(重庆市)、`500200`(重庆市县)。
 
 > ⚠️ **地区填错不会有任何报错,但这家门店会永远收不到款。** 支付宝对**异地收款**有风控,所以平台的**收款链接**(第 6 章的顾客付款页)在下单时会按**顾客 IP 的归属地**自动挑一家同地区的门店去发起收款。地区为空、或与顾客所在地不一致的门店,在挑店时直接落选。上游没有门店更新接口,**建号时填错就补不回来**,只能换一个新的 `third_shop_id` 重新建号。
 
@@ -114,7 +114,7 @@ POST /payment/openapi/alipay/order/create
 | `shop_code` | string | 否 | 平台门店编号(10.1 返回的 `shop_id`) |
 | `third_shop_code` | string | 否 | 你方门店编码,≤32 字符 |
 | `ip` | string | 否 | **不指定门店时**由平台按地区挑店:**顾客的公网 IP**(不是服务器的),精度到市,**优先于 `province_code`**,见下 |
-| `province_code` | string | 否 | 顾客所在省,GB/T 2260 两位码(`"32"` 江苏省、`"11"` 北京市),与 10.1 建店**同一套码**;`ip` 解析不出(或 IP 地区没有可用门店)时的回退,见下 |
+| `province_code` | string | 否 | 顾客所在省,GB/T 2260 六位码(`"320000"` 江苏省、`"110000"` 北京市),与 10.1 建店**同一套码**;`ip` 解析不出(或 IP 地区没有可用门店)时的回退,见下 |
 | `pay_timeout` | int | 否 | 支付过期时间,取值 2–60(单位由上游定义,平台原样透传) |
 | `extra_params` | string | 否 | 透传字段 |
 
@@ -122,10 +122,10 @@ POST /payment/openapi/alipay/order/create
 >
 > - **优先级**:`ip` 能解析出归属地时**以 `ip` 为准**(精确到市);解析不出(内网地址、非大陆 IP、查询失败)或 IP 地区没有可用门店时,**回退用 `province_code` 按全省挑**。两个都传最稳 —— IP 不灵时省码兜底。
 > - **`ip` 填什么**:**顾客发起支付的公网 IP**,不是你服务器的出口 IP、也不是你后台的 IP。格式照常(`"114.114.114.114"`)。
-> - **`province_code` 格式**(照这个填,填错会挑不到门店):GB/T 2260 行政区划代码,与 10.1 建店**完全同一套** —— 省级两位,传的是**代码不是地名**(写 `"32"`,写成 `"江苏省"` 会被拒)。
+> - **`province_code` 格式**(照这个填,填错会挑不到门店):GB/T 2260 行政区划代码完整六位,与 10.1 建店**完全同一套** —— 省级 `XX0000`,传的是**代码不是地名**(写 `"320000"`,写成 `"江苏省"` 会被拒)。
 > - **怎么挑**:先在同市可用门店里挑(市由 `ip` 解析决定);该市没有则回退到全省挑(不报错);同地区有多家时按 `out_biz_no` 稳定挑一家(同一 `out_biz_no` 重复调用落同一家;没传 `out_biz_no` 时随机挑一家)。
 > - **挑中哪家**:响应里的 `third_shop_code` 就是挑中的那家(本来该你传的字段,现在由平台回显)。
-> - **挑不出来会被拒收**(`code=400`,这单不会建立、不会扣款):省码不认识 —— `省份编码不存在: 99`;只传 `ip` 而 IP 解析不出 —— `用户 IP 无法解析出归属地: 1.2.3.4`;IP 地区与省码都挑不到 —— `该地区没有可用门店: 省 32`(`ip` 解析到了市时形如 `该地区没有可用门店: 4301(省 43)`)。「可用门店」= 10.1 建号成功(`create_status=SUCCESS`)且地区与所传编码一致的门店;2026-10-08 之前建的门店没有地区,不会被挑中(见 10.1 的警告)。
+> - **挑不出来会被拒收**(`code=400`,这单不会建立、不会扣款):省码不认识 —— `省份编码不存在: 999999`;只传 `ip` 而 IP 解析不出 —— `用户 IP 无法解析出归属地: 1.2.3.4`;IP 地区与省码都挑不到 —— `该地区没有可用门店: 省 320000`(`ip` 解析到了市时形如 `该地区没有可用门店: 430100(省 430000)`)。「可用门店」= 10.1 建号成功(`create_status=SUCCESS`)且地区与所传编码一致的门店;2026-10-08 之前建的门店没有地区,不会被挑中(见 10.1 的警告)。
 > - **传了任一门店编码时,这两个字段被忽略**(显式指定优先,平台不会用挑出来的店覆盖你指定的店)。
 
 **响应**(B 型,`data` 内)
@@ -333,7 +333,7 @@ Content-Type: application/x-www-form-urlencoded
 ## 11. 门店与订单通道的联调检查清单
 
 - [ ] 认证与签名复用 3.3 的实现,先跑通 3.4 的样例
-- [ ] 建号带了 `province_code` / `city_code`(GB/T 2260),且市码属于该省码
+- [ ] 建号带了 `province_code` / `city_code`(GB/T 2260 完整六位),且市码属于该省码
 - [ ] 不指定门店下单时传了 `ip`(**顾客**的公网 IP)或 `province_code`,且拿响应里的 `third_shop_code` 核对了挑中的门店
 - [ ] `price` 用**元**、写成最简小数(不要 `1.50`、`0.010`)
 - [ ] 下单**不重发** —— 超时先查 10.3,确认没有再决定

+ 2 - 2
frontend/src/views/module_payment/har/shop/index.vue

@@ -350,7 +350,7 @@ async function ensureCities(code?: string | null) {
 }
 
 function onProvinceChange(code: string) {
-  // 换省必须把市清掉:留着一个属于上一省的市码,提交出去就是「省 32 + 市 1101」,
+  // 换省必须把市清掉:留着一个属于上一省的市码,提交出去就是「省 320000 + 市 110100」,
   // 后端按省查表会拒(正确),但用户看到的是自己明明选过城市
   form.city_code = "";
   ensureCities(code);
@@ -455,7 +455,7 @@ async function load() {
     list.value = res.data.data?.items || res.data.data?.list || [];
     total.value = res.data.data?.total || 0;
     // 本页各行涉及的省各拉一次市(有缓存就不重复拉)——列表里要把码显示成名,
-    // 缺这一句时每行只能显示编码,运营得自己背 3201 是哪个市
+    // 缺这一句时每行只能显示编码,运营得自己背 320100 是哪个市
     ensureProvinces();
     new Set(list.value.map((row) => row.province_code).filter((code): code is string => !!code))
       .forEach((code) => ensureCities(code));

+ 2 - 2
java/src/main/java/com/payment/platform/module/payment/har/client/dto/HarShopCreateRequest.java

@@ -19,7 +19,7 @@ public class HarShopCreateRequest {
     private String name;
 
     /**
-     * 省份编码,GB/T 2260 两位({@code 32} = 江苏省)。**上游必填**(规范 5.5,2026-10-08 起)。
+     * 省份编码,GB/T 2260 六位({@code 320000} = 江苏省)。**上游必填**(规范 5.5,2026-10-08 起)。
      *
      * <p>字段名照规范写死:漏了 {@code @JsonProperty} 就会发成 {@code provinceCode},
      * 上游只会回一句参数错误,本地从字段上看不出来 —— 有测试盯报文原文。
@@ -27,7 +27,7 @@ public class HarShopCreateRequest {
     @JsonProperty("province_code")
     private String provinceCode;
 
-    /** 市编码,GB/T 2260 四位({@code 3201} = 南京市)。**上游必填**。见 {@link #provinceCode} */
+    /** 市编码,GB/T 2260 六位({@code 320100} = 南京市)。**上游必填**。见 {@link #provinceCode} */
     @JsonProperty("city_code")
     private String cityCode;
 }

+ 1 - 1
java/src/main/java/com/payment/platform/module/payment/har/controller/HarRegionController.java

@@ -70,7 +70,7 @@ public class HarRegionController {
     /**
      * 某省的地级列表。
      *
-     * @param provinceCode 两位省码,必须是上面那个列表里给出的值
+     * @param provinceCode 省码(GB/T 2260 六位,形如 {@code 320000}),必须是上面那个列表里给出的值
      * @throws BusinessException 400 —— 空白或不在目录里(**不回空表**,理由见类 javadoc)
      */
     @GetMapping("/cities")

+ 1 - 1
java/src/main/java/com/payment/platform/module/payment/har/dto/HarOrderCreateDTO.java

@@ -75,7 +75,7 @@ public class HarOrderCreateDTO {
     /**
      * 顾客所在省(选填,2026-10-09)。**只在没有传任何门店编码时有作用**:给了就由平台
      * 按地区挑一家门店发起交易(挑店规则见 {@code HarShopService#pickForOrder})。
-     * GB/T 2260 省级两位码,如 "32"(江苏省)、"11"(北京市)—— 与门店创建的
+     * GB/T 2260 省级六位码,如 "320000"(江苏省)、"110000"(北京市)—— 与门店创建的
      * province_code 是**同一套码**(10.1 节)。
      *
      * <p>与 {@code ip} 二选一或同传:IP 能解析出归属地时**以 IP 为准**(更精确);

+ 1 - 1
java/src/main/java/com/payment/platform/module/payment/har/dto/HarRegionVO.java

@@ -17,7 +17,7 @@ import lombok.Data;
 @Data
 public class HarRegionVO {
 
-    /** GB/T 2260 编码:省两位({@code 32}),地级四位({@code 3201}) */
+    /** GB/T 2260 编码,完整六位:省 {@code 320000},地级 {@code 320100} */
     private String code;
 
     /** 展示名(目录里那份,直辖市的地级是占位名换成的省名,见 HarRegionCatalog 的 javadoc) */

+ 2 - 2
java/src/main/java/com/payment/platform/module/payment/har/dto/HarShopCreateDTO.java

@@ -41,7 +41,7 @@ public class HarShopCreateDTO {
     private String enterpriseId;
 
     /**
-     * 省份编码,GB/T 2260 两位({@code 32} = 江苏省)。**必填**(2026-10-08 规范 5.5 起)。
+     * 省份编码,GB/T 2260 六位({@code 320000} = 江苏省)。**必填**(2026-10-08 规范 5.5 起)。
      *
      * <p>取值必须是 {@code HarRegionCatalog} 里有的码 —— 服务层会对表校验,
      * 因为编码写错的门店在收款链接里**永远挑不中**,而上游没有门店更新接口,事后补不上。
@@ -51,7 +51,7 @@ public class HarShopCreateDTO {
     @Size(max = 16, message = "省份编码最长 16 位")
     private String provinceCode;
 
-    /** 市编码,GB/T 2260 四位({@code 3201} = 南京市)。**必填**,且必须属于上面那个省。 */
+    /** 市编码,GB/T 2260 六位({@code 320100} = 南京市)。**必填**,且必须属于上面那个省。 */
     @NotBlank(message = "城市不能为空")
     @Size(max = 16, message = "城市编码最长 16 位")
     private String cityCode;

+ 2 - 2
java/src/main/java/com/payment/platform/module/payment/har/dto/HarShopVO.java

@@ -17,9 +17,9 @@ public class HarShopVO {
     private String merchantCode;
     private String thirdShopId;
     private String shopName;
-    /** GB/T 2260 两位省码;2026-10-08 之前建的门店为空 */
+    /** GB/T 2260 六位省码;2026-10-08 之前建的门店为空 */
     private String provinceCode;
-    /** GB/T 2260 四位市码;与 {@link #provinceCode} 同在 */
+    /** GB/T 2260 六位市码;与 {@link #provinceCode} 同在 */
     private String cityCode;
     private String shopId;
     private String createStatus;

+ 2 - 2
java/src/main/java/com/payment/platform/module/payment/har/entity/HarShopEntity.java

@@ -25,7 +25,7 @@ public class HarShopEntity extends PaymentEnterpriseBaseEntity {
     private String shopName;
 
     /**
-     * 所属省份编码,GB/T 2260 两位({@code 32} = 江苏省)。
+     * 所属省份编码,GB/T 2260 六位({@code 320000} = 江苏省)。
      *
      * <p>上游 {@code kf.har.shop.create} 自 2026-10-08 规范起把 province_code / city_code
      * 列为**必填**,且支付宝对异地收款有风控 —— 收款链接下单时要按顾客 IP 挑同地区门店,
@@ -36,7 +36,7 @@ public class HarShopEntity extends PaymentEnterpriseBaseEntity {
      */
     private String provinceCode;
 
-    /** 所属地级编码,GB/T 2260 四位({@code 3201} = 南京市)。见 {@link #provinceCode} */
+    /** 所属地级编码,GB/T 2260 六位({@code 320100} = 南京市)。见 {@link #provinceCode} */
     private String cityCode;
 
     /** 汇安融返回的门店 ID,未成功时为空 */

+ 6 - 5
java/src/main/java/com/payment/platform/module/payment/har/util/HarRegionCatalog.java

@@ -33,7 +33,8 @@ import java.util.Optional;
  * {@code source} 块记着同样的出处与 sha256:
  * <ul>
  *   <li>省市:{@code modood/Administrative-divisions-of-China} 的 {@code dist/provinces.json}
- *       + {@code dist/cities.json}(GB/T 2260 编码);</li>
+ *       + {@code dist/cities.json}(数据集里是短码:省两位、地级四位;生成时统一扩成
+ *       GB/T 2260 完整六位 —— 省 2 位 + 0000、地级 4 位 + 00,与汇安融对照表一致);</li>
  *   <li>繁简:{@code BYVoid/OpenCC} 的 {@code data/dictionary/TSCharacters.txt}(Apache-2.0),
  *       **只取其中指向本目录用字的繁体键**(166 条)。</li>
  * </ul>
@@ -44,8 +45,8 @@ import java.util.Optional;
  * <ol>
  *   <li><b>只含大陆 31 个省级</b>:港澳台不在内。ipwho.is 对它们的 {@code country}
  *       不是「中国」,解析器那一步就先判掉了,这里不需要它们;</li>
- *   <li><b>占位名按省名展示</b>:GB/T 2260 里直辖市的城市码是 1101/1201/3101/5001,
- *       名字是占位的「市辖区」(5002 是「县」)。生成脚本把它们换成省名
+ *   <li><b>占位名按省名展示</b>:GB/T 2260 里直辖市的城市码是 110100/120100/310100/500100,
+ *       名字是占位的「市辖区」(500200 是「县」)。生成脚本把它们换成省名
  *       ({@code 北京市} / {@code 重庆市县})—— 既让下拉能读,也让「北京市 + 北京」
  *       这种回包能命中**市**一级而不必回退到全省;</li>
  *   <li><b>繁简表的键不得是目录自己的字</b>:{@code 沈} 是 {@code 沉} 的繁体变体,
@@ -81,11 +82,11 @@ public class HarRegionCatalog {
     /** 族名:{@code 广西壮族自治区} / {@code 昌吉回族自治州} 去掉它才能与简称对上 */
     private static final List<String> ETHNONYMS = List.of("壮族", "回族", "维吾尔");
 
-    /** 省级。{@code code} 是 GB/T 2260 两位省码。 */
+    /** 省级。{@code code} 是 GB/T 2260 六位省码({@code 320000} = 江苏省)。 */
     public record Province(String code, String name) {
     }
 
-    /** 地级。{@code code} 四位;{@code name} 已是**展示名**(见类 javadoc 第 2 条)。 */
+    /** 地级。{@code code} 是 GB/T 2260 六位市码({@code 320100} = 南京市);{@code name} 已是**展示名**(见类 javadoc 第 2 条)。 */
     public record City(String code, String name, String provinceCode) {
     }
 

+ 19 - 2
java/src/main/resources/db/har-shop-region.sql

@@ -1,4 +1,6 @@
 -- pay_har_shop 新增 province_code / city_code(2026-10-08,规范 261008 5.5 + 异地收款风控)
+-- 2026-10-09 订正:编码统一为 GB/T 2260 完整六位(省 2 位+0000、地级 4 位+00,与汇安融
+-- 对照表一致);用旧短码建的门店见本文件「短码订正」段一次性 UPDATE 过来。
 -- 手工执行: psycopg2 / psql 连接 payment_platform_java 库
 --
 -- 【本脚本尚未执行】执行由用户做。
@@ -31,9 +33,19 @@ ALTER TABLE pay_har_shop ADD COLUMN IF NOT EXISTS province_code varchar(16);
 ALTER TABLE pay_har_shop ADD COLUMN IF NOT EXISTS city_code varchar(16);
 
 COMMENT ON COLUMN pay_har_shop.province_code IS
-    'GB/T 2260 两位省码(32=江苏省)。发上游 province_code(必填),也是按顾客 IP 挑店的匹配键。2026-10-08 之前建的门店为空,且无法回填';
+    'GB/T 2260 六位省码(320000=江苏省)。发上游 province_code(必填),也是按顾客 IP 挑店的匹配键。2026-10-08 之前建的门店为空,且无法回填';
 COMMENT ON COLUMN pay_har_shop.city_code IS
-    'GB/T 2260 四位市码(3201=南京市)。发上游 city_code(必填),见 province_code';
+    'GB/T 2260 六位市码(320100=南京市)。发上游 city_code(必填),见 province_code';
+
+-- ── 短码订正(2026-10-09)──────────────────────────────────────────────
+-- 2026-10-08 到 10-09 之间用旧代码建出的门店,这两列存的是数据集短码(32 / 3201)。
+-- 目录换成六位后它们会**静默落选**(挑店按字符串精确比对),所以在这里补零订正。
+-- 幂等:已经六位的行(length >= 6)与 NULL 行(老店,见上面「存量补不上」)都不动,
+-- 重跑本脚本不会改坏任何一行。HAR 未上线,这段实际只影响测试环境已建的店。
+UPDATE pay_har_shop SET province_code = rpad(province_code, 6, '0')
+ WHERE province_code IS NOT NULL AND length(province_code) < 6;
+UPDATE pay_har_shop SET city_code = rpad(city_code, 6, '0')
+ WHERE city_code IS NOT NULL AND length(city_code) < 6;
 
 -- 挑店那句查询按 merchant_code 过滤(租户谓词由拦截器另加)—— 它在收款链接的
 -- **每一笔支付**上执行,给个索引免得随门店铺开变成顺扫。
@@ -54,3 +66,8 @@ CREATE INDEX IF NOT EXISTS idx_har_shop_merchant_code ON pay_har_shop (merchant_
 -- ③ ★ 有多少存量门店会因缺地区而落选(记下这个数字,它就是要重开的店数)
 -- SELECT count(*) AS 缺地区的店 FROM pay_har_shop
 --  WHERE province_code IS NULL OR city_code IS NULL;
+--
+-- ④ ★ 「短码订正」段跑完不该再有短码(期望 0 —— 非 0 就是 UPDATE 没跑到)
+-- SELECT count(*) AS 剩余短码 FROM pay_har_shop
+--  WHERE (province_code IS NOT NULL AND length(province_code) < 6)
+--     OR (city_code IS NOT NULL AND length(city_code) < 6);

+ 717 - 716
java/src/main/resources/har-region-cn.json

@@ -8,1844 +8,1845 @@
   "t2s": "BYVoid/OpenCC data/dictionary/TSCharacters.txt (Apache-2.0),取其中指向数据集用字的繁体键",
   "t2s_sha256": "9ff46a7d30e5765375eb13d33f2b03a34d298913caf2b120380679f33ae1642d",
   "generated_by": "logs/_har-region-gen.py(不进版本库)",
-  "labels": "GB/T 2260 占位名 市辖区→省名、县→省名+县"
+  "labels": "GB/T 2260 占位名 市辖区→省名、县→省名+县",
+  "codes": "GB/T 2260 完整六位:省 2 位+0000、地级 4 位+00(2026-10-09 按汇安融对照表截图核对;省直辖县级行政区划按同规则推)"
  },
  "provinces": [
   {
-   "code": "11",
+   "code": "110000",
    "name": "北京市"
   },
   {
-   "code": "12",
+   "code": "120000",
    "name": "天津市"
   },
   {
-   "code": "13",
+   "code": "130000",
    "name": "河北省"
   },
   {
-   "code": "14",
+   "code": "140000",
    "name": "山西省"
   },
   {
-   "code": "15",
+   "code": "150000",
    "name": "内蒙古自治区"
   },
   {
-   "code": "21",
+   "code": "210000",
    "name": "辽宁省"
   },
   {
-   "code": "22",
+   "code": "220000",
    "name": "吉林省"
   },
   {
-   "code": "23",
+   "code": "230000",
    "name": "黑龙江省"
   },
   {
-   "code": "31",
+   "code": "310000",
    "name": "上海市"
   },
   {
-   "code": "32",
+   "code": "320000",
    "name": "江苏省"
   },
   {
-   "code": "33",
+   "code": "330000",
    "name": "浙江省"
   },
   {
-   "code": "34",
+   "code": "340000",
    "name": "安徽省"
   },
   {
-   "code": "35",
+   "code": "350000",
    "name": "福建省"
   },
   {
-   "code": "36",
+   "code": "360000",
    "name": "江西省"
   },
   {
-   "code": "37",
+   "code": "370000",
    "name": "山东省"
   },
   {
-   "code": "41",
+   "code": "410000",
    "name": "河南省"
   },
   {
-   "code": "42",
+   "code": "420000",
    "name": "湖北省"
   },
   {
-   "code": "43",
+   "code": "430000",
    "name": "湖南省"
   },
   {
-   "code": "44",
+   "code": "440000",
    "name": "广东省"
   },
   {
-   "code": "45",
+   "code": "450000",
    "name": "广西壮族自治区"
   },
   {
-   "code": "46",
+   "code": "460000",
    "name": "海南省"
   },
   {
-   "code": "50",
+   "code": "500000",
    "name": "重庆市"
   },
   {
-   "code": "51",
+   "code": "510000",
    "name": "四川省"
   },
   {
-   "code": "52",
+   "code": "520000",
    "name": "贵州省"
   },
   {
-   "code": "53",
+   "code": "530000",
    "name": "云南省"
   },
   {
-   "code": "54",
+   "code": "540000",
    "name": "西藏自治区"
   },
   {
-   "code": "61",
+   "code": "610000",
    "name": "陕西省"
   },
   {
-   "code": "62",
+   "code": "620000",
    "name": "甘肃省"
   },
   {
-   "code": "63",
+   "code": "630000",
    "name": "青海省"
   },
   {
-   "code": "64",
+   "code": "640000",
    "name": "宁夏回族自治区"
   },
   {
-   "code": "65",
+   "code": "650000",
    "name": "新疆维吾尔自治区"
   }
  ],
  "cities": [
   {
-   "code": "1101",
+   "code": "110100",
    "name": "北京市",
-   "provinceCode": "11"
+   "provinceCode": "110000"
   },
   {
-   "code": "1201",
+   "code": "120100",
    "name": "天津市",
-   "provinceCode": "12"
+   "provinceCode": "120000"
   },
   {
-   "code": "1301",
+   "code": "130100",
    "name": "石家庄市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1302",
+   "code": "130200",
    "name": "唐山市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1303",
+   "code": "130300",
    "name": "秦皇岛市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1304",
+   "code": "130400",
    "name": "邯郸市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1305",
+   "code": "130500",
    "name": "邢台市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1306",
+   "code": "130600",
    "name": "保定市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1307",
+   "code": "130700",
    "name": "张家口市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1308",
+   "code": "130800",
    "name": "承德市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1309",
+   "code": "130900",
    "name": "沧州市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1310",
+   "code": "131000",
    "name": "廊坊市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1311",
+   "code": "131100",
    "name": "衡水市",
-   "provinceCode": "13"
+   "provinceCode": "130000"
   },
   {
-   "code": "1401",
+   "code": "140100",
    "name": "太原市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1402",
+   "code": "140200",
    "name": "大同市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1403",
+   "code": "140300",
    "name": "阳泉市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1404",
+   "code": "140400",
    "name": "长治市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1405",
+   "code": "140500",
    "name": "晋城市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1406",
+   "code": "140600",
    "name": "朔州市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1407",
+   "code": "140700",
    "name": "晋中市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1408",
+   "code": "140800",
    "name": "运城市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1409",
+   "code": "140900",
    "name": "忻州市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1410",
+   "code": "141000",
    "name": "临汾市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1411",
+   "code": "141100",
    "name": "吕梁市",
-   "provinceCode": "14"
+   "provinceCode": "140000"
   },
   {
-   "code": "1501",
+   "code": "150100",
    "name": "呼和浩特市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1502",
+   "code": "150200",
    "name": "包头市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1503",
+   "code": "150300",
    "name": "乌海市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1504",
+   "code": "150400",
    "name": "赤峰市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1505",
+   "code": "150500",
    "name": "通辽市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1506",
+   "code": "150600",
    "name": "鄂尔多斯市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1507",
+   "code": "150700",
    "name": "呼伦贝尔市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1508",
+   "code": "150800",
    "name": "巴彦淖尔市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1509",
+   "code": "150900",
    "name": "乌兰察布市",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1522",
+   "code": "152200",
    "name": "兴安盟",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1525",
+   "code": "152500",
    "name": "锡林郭勒盟",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "1529",
+   "code": "152900",
    "name": "阿拉善盟",
-   "provinceCode": "15"
+   "provinceCode": "150000"
   },
   {
-   "code": "2101",
+   "code": "210100",
    "name": "沈阳市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2102",
+   "code": "210200",
    "name": "大连市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2103",
+   "code": "210300",
    "name": "鞍山市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2104",
+   "code": "210400",
    "name": "抚顺市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2105",
+   "code": "210500",
    "name": "本溪市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2106",
+   "code": "210600",
    "name": "丹东市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2107",
+   "code": "210700",
    "name": "锦州市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2108",
+   "code": "210800",
    "name": "营口市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2109",
+   "code": "210900",
    "name": "阜新市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2110",
+   "code": "211000",
    "name": "辽阳市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2111",
+   "code": "211100",
    "name": "盘锦市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2112",
+   "code": "211200",
    "name": "铁岭市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2113",
+   "code": "211300",
    "name": "朝阳市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2114",
+   "code": "211400",
    "name": "葫芦岛市",
-   "provinceCode": "21"
+   "provinceCode": "210000"
   },
   {
-   "code": "2201",
+   "code": "220100",
    "name": "长春市",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2202",
+   "code": "220200",
    "name": "吉林市",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2203",
+   "code": "220300",
    "name": "四平市",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2204",
+   "code": "220400",
    "name": "辽源市",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2205",
+   "code": "220500",
    "name": "通化市",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2206",
+   "code": "220600",
    "name": "白山市",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2207",
+   "code": "220700",
    "name": "松原市",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2208",
+   "code": "220800",
    "name": "白城市",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2224",
+   "code": "222400",
    "name": "延边朝鲜族自治州",
-   "provinceCode": "22"
+   "provinceCode": "220000"
   },
   {
-   "code": "2301",
+   "code": "230100",
    "name": "哈尔滨市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2302",
+   "code": "230200",
    "name": "齐齐哈尔市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2303",
+   "code": "230300",
    "name": "鸡西市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2304",
+   "code": "230400",
    "name": "鹤岗市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2305",
+   "code": "230500",
    "name": "双鸭山市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2306",
+   "code": "230600",
    "name": "大庆市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2307",
+   "code": "230700",
    "name": "伊春市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2308",
+   "code": "230800",
    "name": "佳木斯市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2309",
+   "code": "230900",
    "name": "七台河市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2310",
+   "code": "231000",
    "name": "牡丹江市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2311",
+   "code": "231100",
    "name": "黑河市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2312",
+   "code": "231200",
    "name": "绥化市",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "2327",
+   "code": "232700",
    "name": "大兴安岭地区",
-   "provinceCode": "23"
+   "provinceCode": "230000"
   },
   {
-   "code": "3101",
+   "code": "310100",
    "name": "上海市",
-   "provinceCode": "31"
+   "provinceCode": "310000"
   },
   {
-   "code": "3201",
+   "code": "320100",
    "name": "南京市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3202",
+   "code": "320200",
    "name": "无锡市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3203",
+   "code": "320300",
    "name": "徐州市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3204",
+   "code": "320400",
    "name": "常州市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3205",
+   "code": "320500",
    "name": "苏州市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3206",
+   "code": "320600",
    "name": "南通市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3207",
+   "code": "320700",
    "name": "连云港市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3208",
+   "code": "320800",
    "name": "淮安市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3209",
+   "code": "320900",
    "name": "盐城市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3210",
+   "code": "321000",
    "name": "扬州市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3211",
+   "code": "321100",
    "name": "镇江市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3212",
+   "code": "321200",
    "name": "泰州市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3213",
+   "code": "321300",
    "name": "宿迁市",
-   "provinceCode": "32"
+   "provinceCode": "320000"
   },
   {
-   "code": "3301",
+   "code": "330100",
    "name": "杭州市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3302",
+   "code": "330200",
    "name": "宁波市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3303",
+   "code": "330300",
    "name": "温州市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3304",
+   "code": "330400",
    "name": "嘉兴市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3305",
+   "code": "330500",
    "name": "湖州市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3306",
+   "code": "330600",
    "name": "绍兴市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3307",
+   "code": "330700",
    "name": "金华市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3308",
+   "code": "330800",
    "name": "衢州市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3309",
+   "code": "330900",
    "name": "舟山市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3310",
+   "code": "331000",
    "name": "台州市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3311",
+   "code": "331100",
    "name": "丽水市",
-   "provinceCode": "33"
+   "provinceCode": "330000"
   },
   {
-   "code": "3401",
+   "code": "340100",
    "name": "合肥市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3402",
+   "code": "340200",
    "name": "芜湖市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3403",
+   "code": "340300",
    "name": "蚌埠市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3404",
+   "code": "340400",
    "name": "淮南市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3405",
+   "code": "340500",
    "name": "马鞍山市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3406",
+   "code": "340600",
    "name": "淮北市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3407",
+   "code": "340700",
    "name": "铜陵市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3408",
+   "code": "340800",
    "name": "安庆市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3410",
+   "code": "341000",
    "name": "黄山市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3411",
+   "code": "341100",
    "name": "滁州市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3412",
+   "code": "341200",
    "name": "阜阳市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3413",
+   "code": "341300",
    "name": "宿州市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3415",
+   "code": "341500",
    "name": "六安市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3416",
+   "code": "341600",
    "name": "亳州市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3417",
+   "code": "341700",
    "name": "池州市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3418",
+   "code": "341800",
    "name": "宣城市",
-   "provinceCode": "34"
+   "provinceCode": "340000"
   },
   {
-   "code": "3501",
+   "code": "350100",
    "name": "福州市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3502",
+   "code": "350200",
    "name": "厦门市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3503",
+   "code": "350300",
    "name": "莆田市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3504",
+   "code": "350400",
    "name": "三明市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3505",
+   "code": "350500",
    "name": "泉州市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3506",
+   "code": "350600",
    "name": "漳州市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3507",
+   "code": "350700",
    "name": "南平市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3508",
+   "code": "350800",
    "name": "龙岩市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3509",
+   "code": "350900",
    "name": "宁德市",
-   "provinceCode": "35"
+   "provinceCode": "350000"
   },
   {
-   "code": "3601",
+   "code": "360100",
    "name": "南昌市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3602",
+   "code": "360200",
    "name": "景德镇市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3603",
+   "code": "360300",
    "name": "萍乡市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3604",
+   "code": "360400",
    "name": "九江市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3605",
+   "code": "360500",
    "name": "新余市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3606",
+   "code": "360600",
    "name": "鹰潭市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3607",
+   "code": "360700",
    "name": "赣州市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3608",
+   "code": "360800",
    "name": "吉安市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3609",
+   "code": "360900",
    "name": "宜春市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3610",
+   "code": "361000",
    "name": "抚州市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3611",
+   "code": "361100",
    "name": "上饶市",
-   "provinceCode": "36"
+   "provinceCode": "360000"
   },
   {
-   "code": "3701",
+   "code": "370100",
    "name": "济南市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3702",
+   "code": "370200",
    "name": "青岛市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3703",
+   "code": "370300",
    "name": "淄博市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3704",
+   "code": "370400",
    "name": "枣庄市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3705",
+   "code": "370500",
    "name": "东营市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3706",
+   "code": "370600",
    "name": "烟台市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3707",
+   "code": "370700",
    "name": "潍坊市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3708",
+   "code": "370800",
    "name": "济宁市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3709",
+   "code": "370900",
    "name": "泰安市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3710",
+   "code": "371000",
    "name": "威海市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3711",
+   "code": "371100",
    "name": "日照市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3713",
+   "code": "371300",
    "name": "临沂市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3714",
+   "code": "371400",
    "name": "德州市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3715",
+   "code": "371500",
    "name": "聊城市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3716",
+   "code": "371600",
    "name": "滨州市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "3717",
+   "code": "371700",
    "name": "菏泽市",
-   "provinceCode": "37"
+   "provinceCode": "370000"
   },
   {
-   "code": "4101",
+   "code": "410100",
    "name": "郑州市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4102",
+   "code": "410200",
    "name": "开封市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4103",
+   "code": "410300",
    "name": "洛阳市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4104",
+   "code": "410400",
    "name": "平顶山市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4105",
+   "code": "410500",
    "name": "安阳市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4106",
+   "code": "410600",
    "name": "鹤壁市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4107",
+   "code": "410700",
    "name": "新乡市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4108",
+   "code": "410800",
    "name": "焦作市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4109",
+   "code": "410900",
    "name": "濮阳市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4110",
+   "code": "411000",
    "name": "许昌市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4111",
+   "code": "411100",
    "name": "漯河市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4112",
+   "code": "411200",
    "name": "三门峡市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4113",
+   "code": "411300",
    "name": "南阳市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4114",
+   "code": "411400",
    "name": "商丘市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4115",
+   "code": "411500",
    "name": "信阳市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4116",
+   "code": "411600",
    "name": "周口市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4117",
+   "code": "411700",
    "name": "驻马店市",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4190",
+   "code": "419000",
    "name": "省直辖县级行政区划",
-   "provinceCode": "41"
+   "provinceCode": "410000"
   },
   {
-   "code": "4201",
+   "code": "420100",
    "name": "武汉市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4202",
+   "code": "420200",
    "name": "黄石市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4203",
+   "code": "420300",
    "name": "十堰市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4205",
+   "code": "420500",
    "name": "宜昌市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4206",
+   "code": "420600",
    "name": "襄阳市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4207",
+   "code": "420700",
    "name": "鄂州市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4208",
+   "code": "420800",
    "name": "荆门市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4209",
+   "code": "420900",
    "name": "孝感市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4210",
+   "code": "421000",
    "name": "荆州市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4211",
+   "code": "421100",
    "name": "黄冈市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4212",
+   "code": "421200",
    "name": "咸宁市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4213",
+   "code": "421300",
    "name": "随州市",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4228",
+   "code": "422800",
    "name": "恩施土家族苗族自治州",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4290",
+   "code": "429000",
    "name": "省直辖县级行政区划",
-   "provinceCode": "42"
+   "provinceCode": "420000"
   },
   {
-   "code": "4301",
+   "code": "430100",
    "name": "长沙市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4302",
+   "code": "430200",
    "name": "株洲市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4303",
+   "code": "430300",
    "name": "湘潭市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4304",
+   "code": "430400",
    "name": "衡阳市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4305",
+   "code": "430500",
    "name": "邵阳市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4306",
+   "code": "430600",
    "name": "岳阳市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4307",
+   "code": "430700",
    "name": "常德市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4308",
+   "code": "430800",
    "name": "张家界市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4309",
+   "code": "430900",
    "name": "益阳市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4310",
+   "code": "431000",
    "name": "郴州市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4311",
+   "code": "431100",
    "name": "永州市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4312",
+   "code": "431200",
    "name": "怀化市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4313",
+   "code": "431300",
    "name": "娄底市",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4331",
+   "code": "433100",
    "name": "湘西土家族苗族自治州",
-   "provinceCode": "43"
+   "provinceCode": "430000"
   },
   {
-   "code": "4401",
+   "code": "440100",
    "name": "广州市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4402",
+   "code": "440200",
    "name": "韶关市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4403",
+   "code": "440300",
    "name": "深圳市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4404",
+   "code": "440400",
    "name": "珠海市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4405",
+   "code": "440500",
    "name": "汕头市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4406",
+   "code": "440600",
    "name": "佛山市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4407",
+   "code": "440700",
    "name": "江门市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4408",
+   "code": "440800",
    "name": "湛江市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4409",
+   "code": "440900",
    "name": "茂名市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4412",
+   "code": "441200",
    "name": "肇庆市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4413",
+   "code": "441300",
    "name": "惠州市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4414",
+   "code": "441400",
    "name": "梅州市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4415",
+   "code": "441500",
    "name": "汕尾市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4416",
+   "code": "441600",
    "name": "河源市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4417",
+   "code": "441700",
    "name": "阳江市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4418",
+   "code": "441800",
    "name": "清远市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4419",
+   "code": "441900",
    "name": "东莞市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4420",
+   "code": "442000",
    "name": "中山市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4451",
+   "code": "445100",
    "name": "潮州市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4452",
+   "code": "445200",
    "name": "揭阳市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4453",
+   "code": "445300",
    "name": "云浮市",
-   "provinceCode": "44"
+   "provinceCode": "440000"
   },
   {
-   "code": "4501",
+   "code": "450100",
    "name": "南宁市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4502",
+   "code": "450200",
    "name": "柳州市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4503",
+   "code": "450300",
    "name": "桂林市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4504",
+   "code": "450400",
    "name": "梧州市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4505",
+   "code": "450500",
    "name": "北海市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4506",
+   "code": "450600",
    "name": "防城港市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4507",
+   "code": "450700",
    "name": "钦州市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4508",
+   "code": "450800",
    "name": "贵港市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4509",
+   "code": "450900",
    "name": "玉林市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4510",
+   "code": "451000",
    "name": "百色市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4511",
+   "code": "451100",
    "name": "贺州市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4512",
+   "code": "451200",
    "name": "河池市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4513",
+   "code": "451300",
    "name": "来宾市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4514",
+   "code": "451400",
    "name": "崇左市",
-   "provinceCode": "45"
+   "provinceCode": "450000"
   },
   {
-   "code": "4601",
+   "code": "460100",
    "name": "海口市",
-   "provinceCode": "46"
+   "provinceCode": "460000"
   },
   {
-   "code": "4602",
+   "code": "460200",
    "name": "三亚市",
-   "provinceCode": "46"
+   "provinceCode": "460000"
   },
   {
-   "code": "4603",
+   "code": "460300",
    "name": "三沙市",
-   "provinceCode": "46"
+   "provinceCode": "460000"
   },
   {
-   "code": "4604",
+   "code": "460400",
    "name": "儋州市",
-   "provinceCode": "46"
+   "provinceCode": "460000"
   },
   {
-   "code": "4690",
+   "code": "469000",
    "name": "省直辖县级行政区划",
-   "provinceCode": "46"
+   "provinceCode": "460000"
   },
   {
-   "code": "5001",
+   "code": "500100",
    "name": "重庆市",
-   "provinceCode": "50"
+   "provinceCode": "500000"
   },
   {
-   "code": "5002",
+   "code": "500200",
    "name": "重庆市县",
-   "provinceCode": "50"
+   "provinceCode": "500000"
   },
   {
-   "code": "5101",
+   "code": "510100",
    "name": "成都市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5103",
+   "code": "510300",
    "name": "自贡市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5104",
+   "code": "510400",
    "name": "攀枝花市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5105",
+   "code": "510500",
    "name": "泸州市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5106",
+   "code": "510600",
    "name": "德阳市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5107",
+   "code": "510700",
    "name": "绵阳市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5108",
+   "code": "510800",
    "name": "广元市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5109",
+   "code": "510900",
    "name": "遂宁市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5110",
+   "code": "511000",
    "name": "内江市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5111",
+   "code": "511100",
    "name": "乐山市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5113",
+   "code": "511300",
    "name": "南充市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5114",
+   "code": "511400",
    "name": "眉山市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5115",
+   "code": "511500",
    "name": "宜宾市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5116",
+   "code": "511600",
    "name": "广安市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5117",
+   "code": "511700",
    "name": "达州市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5118",
+   "code": "511800",
    "name": "雅安市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5119",
+   "code": "511900",
    "name": "巴中市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5120",
+   "code": "512000",
    "name": "资阳市",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5132",
+   "code": "513200",
    "name": "阿坝藏族羌族自治州",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5133",
+   "code": "513300",
    "name": "甘孜藏族自治州",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5134",
+   "code": "513400",
    "name": "凉山彝族自治州",
-   "provinceCode": "51"
+   "provinceCode": "510000"
   },
   {
-   "code": "5201",
+   "code": "520100",
    "name": "贵阳市",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5202",
+   "code": "520200",
    "name": "六盘水市",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5203",
+   "code": "520300",
    "name": "遵义市",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5204",
+   "code": "520400",
    "name": "安顺市",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5205",
+   "code": "520500",
    "name": "毕节市",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5206",
+   "code": "520600",
    "name": "铜仁市",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5223",
+   "code": "522300",
    "name": "黔西南布依族苗族自治州",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5226",
+   "code": "522600",
    "name": "黔东南苗族侗族自治州",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5227",
+   "code": "522700",
    "name": "黔南布依族苗族自治州",
-   "provinceCode": "52"
+   "provinceCode": "520000"
   },
   {
-   "code": "5301",
+   "code": "530100",
    "name": "昆明市",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5303",
+   "code": "530300",
    "name": "曲靖市",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5304",
+   "code": "530400",
    "name": "玉溪市",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5305",
+   "code": "530500",
    "name": "保山市",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5306",
+   "code": "530600",
    "name": "昭通市",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5307",
+   "code": "530700",
    "name": "丽江市",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5308",
+   "code": "530800",
    "name": "普洱市",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5309",
+   "code": "530900",
    "name": "临沧市",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5323",
+   "code": "532300",
    "name": "楚雄彝族自治州",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5325",
+   "code": "532500",
    "name": "红河哈尼族彝族自治州",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5326",
+   "code": "532600",
    "name": "文山壮族苗族自治州",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5328",
+   "code": "532800",
    "name": "西双版纳傣族自治州",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5329",
+   "code": "532900",
    "name": "大理白族自治州",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5331",
+   "code": "533100",
    "name": "德宏傣族景颇族自治州",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5333",
+   "code": "533300",
    "name": "怒江傈僳族自治州",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5334",
+   "code": "533400",
    "name": "迪庆藏族自治州",
-   "provinceCode": "53"
+   "provinceCode": "530000"
   },
   {
-   "code": "5401",
+   "code": "540100",
    "name": "拉萨市",
-   "provinceCode": "54"
+   "provinceCode": "540000"
   },
   {
-   "code": "5402",
+   "code": "540200",
    "name": "日喀则市",
-   "provinceCode": "54"
+   "provinceCode": "540000"
   },
   {
-   "code": "5403",
+   "code": "540300",
    "name": "昌都市",
-   "provinceCode": "54"
+   "provinceCode": "540000"
   },
   {
-   "code": "5404",
+   "code": "540400",
    "name": "林芝市",
-   "provinceCode": "54"
+   "provinceCode": "540000"
   },
   {
-   "code": "5405",
+   "code": "540500",
    "name": "山南市",
-   "provinceCode": "54"
+   "provinceCode": "540000"
   },
   {
-   "code": "5406",
+   "code": "540600",
    "name": "那曲市",
-   "provinceCode": "54"
+   "provinceCode": "540000"
   },
   {
-   "code": "5425",
+   "code": "542500",
    "name": "阿里地区",
-   "provinceCode": "54"
+   "provinceCode": "540000"
   },
   {
-   "code": "6101",
+   "code": "610100",
    "name": "西安市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6102",
+   "code": "610200",
    "name": "铜川市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6103",
+   "code": "610300",
    "name": "宝鸡市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6104",
+   "code": "610400",
    "name": "咸阳市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6105",
+   "code": "610500",
    "name": "渭南市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6106",
+   "code": "610600",
    "name": "延安市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6107",
+   "code": "610700",
    "name": "汉中市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6108",
+   "code": "610800",
    "name": "榆林市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6109",
+   "code": "610900",
    "name": "安康市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6110",
+   "code": "611000",
    "name": "商洛市",
-   "provinceCode": "61"
+   "provinceCode": "610000"
   },
   {
-   "code": "6201",
+   "code": "620100",
    "name": "兰州市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6202",
+   "code": "620200",
    "name": "嘉峪关市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6203",
+   "code": "620300",
    "name": "金昌市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6204",
+   "code": "620400",
    "name": "白银市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6205",
+   "code": "620500",
    "name": "天水市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6206",
+   "code": "620600",
    "name": "武威市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6207",
+   "code": "620700",
    "name": "张掖市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6208",
+   "code": "620800",
    "name": "平凉市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6209",
+   "code": "620900",
    "name": "酒泉市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6210",
+   "code": "621000",
    "name": "庆阳市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6211",
+   "code": "621100",
    "name": "定西市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6212",
+   "code": "621200",
    "name": "陇南市",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6229",
+   "code": "622900",
    "name": "临夏回族自治州",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6230",
+   "code": "623000",
    "name": "甘南藏族自治州",
-   "provinceCode": "62"
+   "provinceCode": "620000"
   },
   {
-   "code": "6301",
+   "code": "630100",
    "name": "西宁市",
-   "provinceCode": "63"
+   "provinceCode": "630000"
   },
   {
-   "code": "6302",
+   "code": "630200",
    "name": "海东市",
-   "provinceCode": "63"
+   "provinceCode": "630000"
   },
   {
-   "code": "6322",
+   "code": "632200",
    "name": "海北藏族自治州",
-   "provinceCode": "63"
+   "provinceCode": "630000"
   },
   {
-   "code": "6323",
+   "code": "632300",
    "name": "黄南藏族自治州",
-   "provinceCode": "63"
+   "provinceCode": "630000"
   },
   {
-   "code": "6325",
+   "code": "632500",
    "name": "海南藏族自治州",
-   "provinceCode": "63"
+   "provinceCode": "630000"
   },
   {
-   "code": "6326",
+   "code": "632600",
    "name": "果洛藏族自治州",
-   "provinceCode": "63"
+   "provinceCode": "630000"
   },
   {
-   "code": "6327",
+   "code": "632700",
    "name": "玉树藏族自治州",
-   "provinceCode": "63"
+   "provinceCode": "630000"
   },
   {
-   "code": "6328",
+   "code": "632800",
    "name": "海西蒙古族藏族自治州",
-   "provinceCode": "63"
+   "provinceCode": "630000"
   },
   {
-   "code": "6401",
+   "code": "640100",
    "name": "银川市",
-   "provinceCode": "64"
+   "provinceCode": "640000"
   },
   {
-   "code": "6402",
+   "code": "640200",
    "name": "石嘴山市",
-   "provinceCode": "64"
+   "provinceCode": "640000"
   },
   {
-   "code": "6403",
+   "code": "640300",
    "name": "吴忠市",
-   "provinceCode": "64"
+   "provinceCode": "640000"
   },
   {
-   "code": "6404",
+   "code": "640400",
    "name": "固原市",
-   "provinceCode": "64"
+   "provinceCode": "640000"
   },
   {
-   "code": "6405",
+   "code": "640500",
    "name": "中卫市",
-   "provinceCode": "64"
+   "provinceCode": "640000"
   },
   {
-   "code": "6501",
+   "code": "650100",
    "name": "乌鲁木齐市",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6502",
+   "code": "650200",
    "name": "克拉玛依市",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6504",
+   "code": "650400",
    "name": "吐鲁番市",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6505",
+   "code": "650500",
    "name": "哈密市",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6523",
+   "code": "652300",
    "name": "昌吉回族自治州",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6527",
+   "code": "652700",
    "name": "博尔塔拉蒙古自治州",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6528",
+   "code": "652800",
    "name": "巴音郭楞蒙古自治州",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6529",
+   "code": "652900",
    "name": "阿克苏地区",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6530",
+   "code": "653000",
    "name": "克孜勒苏柯尔克孜自治州",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6531",
+   "code": "653100",
    "name": "喀什地区",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6532",
+   "code": "653200",
    "name": "和田地区",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6540",
+   "code": "654000",
    "name": "伊犁哈萨克自治州",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6542",
+   "code": "654200",
    "name": "塔城地区",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6543",
+   "code": "654300",
    "name": "阿勒泰地区",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   },
   {
-   "code": "6590",
+   "code": "659000",
    "name": "自治区直辖县级行政区划",
-   "provinceCode": "65"
+   "provinceCode": "650000"
   }
  ],
  "t2s": {

+ 9 - 9
java/src/test/java/com/payment/platform/module/payment/har/controller/HarCheckoutControllerTest.java

@@ -107,7 +107,7 @@ class HarCheckoutControllerTest {
     }
 
     /**
-     * 让这条腿一路走到下单:IP 解析成江苏南京,并按 (省 32, 市 3201) 挑到一家店。
+     * 让这条腿一路走到下单:IP 解析成江苏南京,并按 (省 320000, 市 320100) 挑到一家店。
      *
      * @return 被挑中的那家门店(用例要断言它的 {@code third_shop_code} 时取用)
      */
@@ -115,7 +115,7 @@ class HarCheckoutControllerTest {
         when(ipRegionResolver.lookup(IP)).thenReturn(Optional.of(new IpGeo("CN", "江苏省", "JS", "南京")));
         HarShopEntity shop = new HarShopEntity();
         shop.setThirdShopId("SHOP-NJ");
-        when(shopService.pickForRegion(MCH, "32", "3201", IP)).thenReturn(shop);
+        when(shopService.pickForRegion(MCH, "320000", "320100", IP)).thenReturn(shop);
         return shop;
     }
 
@@ -262,7 +262,7 @@ class HarCheckoutControllerTest {
      * **√ 必填**,{@code shop_code}(平台门店编号)才是选填;而按顾客所在地挑店正是本功能的
      * 全部意义 —— 用错地区的门店下单,支付宝会按异地收款风控拦。
      *
-     * <p>stub 写的是**精确匹配**的 {@code (MCH,"32","3201",IP)} ⇒ 参数错一个,它返回 null,
+     * <p>stub 写的是**精确匹配**的 {@code (MCH,"320000","320100",IP)} ⇒ 参数错一个,它返回 null,
      * 这条腿当场 400,断言立刻红。
      */
     @Test
@@ -276,7 +276,7 @@ class HarCheckoutControllerTest {
         ArgumentCaptor<HarOrderCreateDTO> captor = ArgumentCaptor.forClass(HarOrderCreateDTO.class);
         verify(orderService).create(captor.capture(), isNull(), isNull());
         assertEquals("SHOP-NJ", captor.getValue().getThirdShopCode());
-        verify(shopService).pickForRegion(MCH, "32", "3201", IP);
+        verify(shopService).pickForRegion(MCH, "320000", "320100", IP);
     }
 
     /**
@@ -288,7 +288,7 @@ class HarCheckoutControllerTest {
         when(linkService.requireByToken("tok")).thenReturn(link());
         when(ipRegionResolver.lookup(IP))
                 .thenReturn(Optional.of(new IpGeo("CN", "重庆市", "CQ", "渝中區")));
-        when(shopService.pickForRegion(MCH, "50", null, IP)).thenReturn(shopWith("SHOP-CQ"));
+        when(shopService.pickForRegion(MCH, "500000", null, IP)).thenReturn(shopWith("SHOP-CQ"));
         when(orderService.create(any(), any(), any())).thenReturn(paidVo("https://pay/x"));
 
         controller.create("tok", pay("5"), request());
@@ -304,7 +304,7 @@ class HarCheckoutControllerTest {
         when(linkService.requireByToken("tok")).thenReturn(link());
         when(ipRegionResolver.lookup(IP))
                 .thenReturn(Optional.of(new IpGeo("CN", "西藏自治區", "XZ", "拉薩")));
-        when(shopService.pickForRegion(MCH, "54", "5401", IP)).thenReturn(shopWith("SHOP-XZ"));
+        when(shopService.pickForRegion(MCH, "540000", "540100", IP)).thenReturn(shopWith("SHOP-XZ"));
         when(orderService.create(any(), any(), any())).thenReturn(paidVo("https://pay/x"));
 
         controller.create("tok", pay("5"), request());
@@ -357,7 +357,7 @@ class HarCheckoutControllerTest {
     void create_noUsableShopInThatRegion_400_distinctWording() {
         when(linkService.requireByToken("tok")).thenReturn(link());
         when(ipRegionResolver.lookup(IP)).thenReturn(Optional.of(new IpGeo("CN", "江苏省", "JS", "南京")));
-        when(shopService.pickForRegion(MCH, "32", "3201", IP)).thenReturn(null);
+        when(shopService.pickForRegion(MCH, "320000", "320100", IP)).thenReturn(null);
 
         BusinessException ex = assertThrows(BusinessException.class,
                 () -> controller.create("tok", pay("5"), request()));
@@ -377,7 +377,7 @@ class HarCheckoutControllerTest {
     void create_shopPickRunsUnderTheLinkTenant() {
         when(linkService.requireByToken("tok")).thenReturn(link());
         when(ipRegionResolver.lookup(IP)).thenReturn(Optional.of(new IpGeo("CN", "江苏省", "JS", "南京")));
-        when(shopService.pickForRegion(MCH, "32", "3201", IP)).thenAnswer(inv -> {
+        when(shopService.pickForRegion(MCH, "320000", "320100", IP)).thenAnswer(inv -> {
             assertEquals(TENANT, currentTenantId(), "挑店读的是 pay_har_shop,必须带租户");
             return shopWith("SHOP-NJ");
         });
@@ -439,7 +439,7 @@ class HarCheckoutControllerTest {
             return null;
         });
         // 挑店读的是 pay_har_shop,它也在 CONDITIONAL_TABLES 里 —— 这一跳同样要在租户身份下。
-        when(shopService.pickForRegion(MCH, "32", "3201", IP)).thenAnswer(inv -> {
+        when(shopService.pickForRegion(MCH, "320000", "320100", IP)).thenAnswer(inv -> {
             seen.add(currentTenantId());
             return shopWith("SHOP-NJ");
         });

+ 16 - 16
java/src/test/java/com/payment/platform/module/payment/har/controller/HarRegionControllerTest.java

@@ -44,7 +44,7 @@ class HarRegionControllerTest {
     private final HarRegionCatalog catalog = new HarRegionCatalog();
     private final HarRegionController controller = new HarRegionController(catalog);
 
-    /** 按码取名字;取不到就把**实际拿到的码**全打出来 —— 否则失败信息只有一句「没有 32」 */
+    /** 按码取名字;取不到就把**实际拿到的码**全打出来 —— 否则失败信息只有一句「没有 320000」 */
     private static String nameOf(List<HarRegionVO> rows, String code) {
         return rows.stream()
                 .filter(v -> code.equals(v.getCode()))
@@ -66,11 +66,11 @@ class HarRegionControllerTest {
                 "省级 31 个。港澳台不在内是有意的:ipwho.is 对它们的 country_code 不是 CN,"
                         + "解析那一步就先判掉了(见 HarIpRegionResolver 的 javadoc)");
         // 抽三个点:首都、本功能最常用的那个省、名字最长的一个(自治区的归一化专挑它)
-        assertEquals("北京市", nameOf(rows, "11"));
-        assertEquals("江苏省", nameOf(rows, "32"));
-        assertEquals("新疆维吾尔自治区", nameOf(rows, "65"));
-        assertTrue(rows.stream().allMatch(v -> v.getCode() != null && v.getCode().length() == 2),
-                "两位省码是 GB/T 2260 的形状;选择器回填的就是这一格,缺一个字符都拼不上");
+        assertEquals("北京市", nameOf(rows, "110000"));
+        assertEquals("江苏省", nameOf(rows, "320000"));
+        assertEquals("新疆维吾尔自治区", nameOf(rows, "650000"));
+        assertTrue(rows.stream().allMatch(v -> v.getCode() != null && v.getCode().length() == 6),
+                "六位省码是 GB/T 2260 完整码的形状;选择器回填的就是这一格,缺一个字符都拼不上");
         assertTrue(rows.stream().allMatch(v -> v.getName() != null && !v.getName().isBlank()),
                 "名字要直接上屏,空名字在下拉里是一个看不见的空行");
     }
@@ -79,26 +79,26 @@ class HarRegionControllerTest {
 
     @Test
     void cities_returnsOnlyThatProvincesCities() {
-        Result<List<HarRegionVO>> r = controller.cities("32");
+        Result<List<HarRegionVO>> r = controller.cities("320000");
         List<HarRegionVO> rows = r.getData();
 
         assertEquals(0, r.getCode());
         assertEquals(13, rows.size(), "江苏 13 个地级");
-        assertEquals("南京市", nameOf(rows, "3201"));
-        assertTrue(rows.stream().allMatch(v -> v.getCode().length() == 4),
-                "四位市码");
+        assertEquals("南京市", nameOf(rows, "320100"));
+        assertTrue(rows.stream().allMatch(v -> v.getCode().length() == 6),
+                "六位市码");
         assertTrue(rows.stream().allMatch(v -> v.getCode().startsWith("32")),
                 "返回的必须是**该省**的市:这是「按省取市」这件事本身");
 
         // ★ 正对照:3401(合肥)在目录里确实存在 —— 少了这一句,上面那条否定断言
         //   在「目录里根本没有 3401」时也会绿(那就是一条空断言)。
-        assertTrue(catalog.cities("34").stream().anyMatch(c -> "3401".equals(c.code())),
-                "正对照:3401 是安徽的合肥市,目录里有");
-        assertFalse(rows.stream().anyMatch(v -> "3401".equals(v.getCode())),
+        assertTrue(catalog.cities("340000").stream().anyMatch(c -> "340100".equals(c.code())),
+                "正对照:340100 是安徽的合肥市,目录里有");
+        assertFalse(rows.stream().anyMatch(v -> "340100".equals(v.getCode())),
                 "安徽的市不许出现在江苏的列表里");
 
         // 空白是查询串里最常见的手滑(手填 URL / 复制粘贴带空格)—— 认得出就别报 400
-        assertEquals(13, controller.cities(" 32 ").getData().size(),
+        assertEquals(13, controller.cities(" 320000 ").getData().size(),
                 "带空格的省码要先 trim 再查表,不能直接拿去比对");
     }
 
@@ -110,10 +110,10 @@ class HarRegionControllerTest {
      */
     @Test
     void cities_unknownProvinceCode_400_notAnEmptyList() {
-        BusinessException ex = assertThrows(BusinessException.class, () -> controller.cities("99"));
+        BusinessException ex = assertThrows(BusinessException.class, () -> controller.cities("999999"));
 
         assertEquals(400, ex.getCode());
-        assertEquals("省份编码不存在: 99", ex.getMessage(),
+        assertEquals("省份编码不存在: 999999", ex.getMessage(),
                 "措辞与 HarShopService#requireKnownCity 同源 —— 同一类错误在两处该是同一句话");
     }
 

+ 4 - 4
java/src/test/java/com/payment/platform/module/payment/har/dto/HarOrderCreateJsonContractTest.java

@@ -46,7 +46,7 @@ class HarOrderCreateJsonContractTest {
     @Test
     void orderCreateRequestFields() {
         HarOrderCreateDTO d = new HarOrderCreateDTO();
-        d.setProvinceCode("32");
+        d.setProvinceCode("320000");
         d.setIp("114.114.114.114");
 
         assertEquals(Set.of("enterprise_id", "merchant_code", "out_biz_no", "price", "title",
@@ -59,9 +59,9 @@ class HarOrderCreateJsonContractTest {
     @Test
     void regionFieldsBindFromTheSnakeCaseNamesTheDocQuotes() throws Exception {
         HarOrderCreateDTO d = mapper.readValue(
-                "{\"province_code\":\"32\",\"ip\":\"114.114.114.114\"}", HarOrderCreateDTO.class);
+                "{\"province_code\":\"320000\",\"ip\":\"114.114.114.114\"}", HarOrderCreateDTO.class);
 
-        assertEquals("32", d.getProvinceCode());
+        assertEquals("320000", d.getProvinceCode());
         assertEquals("114.114.114.114", d.getIp());
     }
 
@@ -74,7 +74,7 @@ class HarOrderCreateJsonContractTest {
     @Test
     void camelCaseNamesDoNotBindSilently() {
         assertThrows(UnrecognizedPropertyException.class, () -> mapper.readValue(
-                "{\"provinceCode\":\"32\"}", HarOrderCreateDTO.class));
+                "{\"provinceCode\":\"320000\"}", HarOrderCreateDTO.class));
     }
 
     /**

+ 9 - 9
java/src/test/java/com/payment/platform/module/payment/har/service/HarOrderServiceTest.java

@@ -430,12 +430,12 @@ class HarOrderServiceTest {
     @Test
     void create_withIpAndProvinceCode_passesBothToThePick() {
         HarOrderCreateDTO dto = dto();
-        dto.setProvinceCode("32");
+        dto.setProvinceCode("320000");
         dto.setIp("114.114.114.114");
         HarShopEntity picked = new HarShopEntity();
         picked.setThirdShopId("NJ");
         when(merchantService.requireByEnterpriseId("E1")).thenReturn(merchant());
-        when(shopService.pickForOrder("M001", "32", "114.114.114.114", "BIZ-0001")).thenReturn(picked);
+        when(shopService.pickForOrder("M001", "320000", "114.114.114.114", "BIZ-0001")).thenReturn(picked);
         when(harClient.precreate(any())).thenReturn(response("10000", precreateDetail()));
 
         service.create(dto, "E1", "KEY1");
@@ -455,11 +455,11 @@ class HarOrderServiceTest {
     void create_scanMode_alsoSendsThePickedShop() {
         HarOrderCreateDTO dto = dto();
         dto.setAuthCode("280812345678901234");
-        dto.setProvinceCode("32");
+        dto.setProvinceCode("320000");
         HarShopEntity picked = new HarShopEntity();
         picked.setThirdShopId("NJ");
         when(merchantService.requireByEnterpriseId("E1")).thenReturn(merchant());
-        when(shopService.pickForOrder("M001", "32", null, "BIZ-0001")).thenReturn(picked);
+        when(shopService.pickForOrder("M001", "320000", null, "BIZ-0001")).thenReturn(picked);
         when(harClient.pay(any())).thenReturn(response("10000", precreateDetail()));
 
         service.create(dto, "E1", "KEY1");
@@ -474,7 +474,7 @@ class HarOrderServiceTest {
     void create_explicitThirdShopCode_beatsRegionCodes() {
         HarOrderCreateDTO dto = dto();
         dto.setThirdShopCode("EXPLICIT");
-        dto.setProvinceCode("32");
+        dto.setProvinceCode("320000");
         when(merchantService.requireByEnterpriseId("E1")).thenReturn(merchant());
         when(harClient.precreate(any())).thenReturn(response("10000", precreateDetail()));
 
@@ -494,7 +494,7 @@ class HarOrderServiceTest {
     void create_explicitShopCode_beatsRegionCodes() {
         HarOrderCreateDTO dto = dto();
         dto.setShopCode("S-PLATFORM");
-        dto.setProvinceCode("32");
+        dto.setProvinceCode("320000");
         when(merchantService.requireByEnterpriseId("E1")).thenReturn(merchant());
         when(harClient.precreate(any())).thenReturn(response("10000", precreateDetail()));
 
@@ -515,10 +515,10 @@ class HarOrderServiceTest {
     @Test
     void create_regionPickRejected_400_beforePersistAndUpstream() {
         HarOrderCreateDTO dto = dto();
-        dto.setProvinceCode("32");
+        dto.setProvinceCode("320000");
         when(merchantService.requireByEnterpriseId("E1")).thenReturn(merchant());
-        when(shopService.pickForOrder("M001", "32", null, "BIZ-0001"))
-                .thenThrow(new BusinessException(400, "该地区没有可用门店: 省 32"));
+        when(shopService.pickForOrder("M001", "320000", null, "BIZ-0001"))
+                .thenThrow(new BusinessException(400, "该地区没有可用门店: 省 320000"));
 
         BusinessException e = assertThrows(BusinessException.class, () -> service.create(dto, "E1", "KEY1"));
 

+ 78 - 78
java/src/test/java/com/payment/platform/module/payment/har/service/HarShopRegionPickTest.java

@@ -82,21 +82,21 @@ class HarShopRegionPickTest {
     @Test
     void pick_cityTierWins() {
         List<HarShopEntity> candidates = List.of(
-                ok("NJ", "32", "3201"),
-                ok("SZ", "32", "3205"));
+                ok("NJ", "320000", "320100"),
+                ok("SZ", "320000", "320500"));
 
-        assertEquals("NJ", pick(candidates, "32", "3201", "114.114.114.114").getThirdShopId());
+        assertEquals("NJ", pick(candidates, "320000", "320100", "114.114.114.114").getThirdShopId());
     }
 
     /** 同城没店 ⇒ 退到同省,且**只**在省内挑(异省的店绝不越界) */
     @Test
     void pick_cityMissing_fallsBackToTheSameProvinceOnly() {
         List<HarShopEntity> candidates = List.of(
-                ok("SZ", "32", "3205"),
-                ok("WX", "32", "3202"),
-                ok("HZ", "33", "3301"));
+                ok("SZ", "320000", "320500"),
+                ok("WX", "320000", "320200"),
+                ok("HZ", "330000", "330100"));
 
-        HarShopEntity picked = pick(candidates, "32", "3201", "114.114.114.114");
+        HarShopEntity picked = pick(candidates, "320000", "320100", "114.114.114.114");
 
         assertNotNull(picked);
         assertTrue(Set.of("SZ", "WX").contains(picked.getThirdShopId()),
@@ -109,27 +109,27 @@ class HarShopRegionPickTest {
      */
     @Test
     void pick_cityCodeUnderAnotherProvinceIsNotTrusted() {
-        List<HarShopEntity> candidates = List.of(ok("X", "99", "3201"));
+        List<HarShopEntity> candidates = List.of(ok("X", "999999", "320100"));
 
-        assertNull(pick(candidates, "32", "3201", "114.114.114.114"));
+        assertNull(pick(candidates, "320000", "320100", "114.114.114.114"));
     }
 
     @Test
     void pick_blankProvinceCode_picksNothing() {
-        List<HarShopEntity> candidates = List.of(ok("NJ", "32", "3201"));
+        List<HarShopEntity> candidates = List.of(ok("NJ", "320000", "320100"));
 
-        assertNull(pick(candidates, null, "3201", "114.114.114.114"));
-        assertNull(pick(candidates, "  ", "3201", "114.114.114.114"));
+        assertNull(pick(candidates, null, "320100", "114.114.114.114"));
+        assertNull(pick(candidates, "  ", "320100", "114.114.114.114"));
         // 正控:同一个候选集,省码给对就必须挑得出来(否则上两条是量具坏了在假绿)
-        assertNotNull(pick(candidates, "32", "3201", "114.114.114.114"));
+        assertNotNull(pick(candidates, "320000", "320100", "114.114.114.114"));
     }
 
     /** 空 city 只走省级(ipwho.is 偶尔只给到省) */
     @Test
     void pick_blankCityCode_stillUsesTheProvinceTier() {
-        List<HarShopEntity> candidates = List.of(ok("NJ", "32", "3201"));
+        List<HarShopEntity> candidates = List.of(ok("NJ", "320000", "320100"));
 
-        assertEquals("NJ", pick(candidates, "32", null, "114.114.114.114").getThirdShopId());
+        assertEquals("NJ", pick(candidates, "320000", null, "114.114.114.114").getThirdShopId());
     }
 
     // ──────────────────────────── 什么算「能用的店」 ────────────────────────────
@@ -142,31 +142,31 @@ class HarShopRegionPickTest {
     @Test
     void pick_onlyConsidersSuccessfullyCreatedShops() {
         List<HarShopEntity> unusable = List.of(
-                shop("FAILED", "32", "3201", HarShopCreateStatus.FAILED, "S1"),
-                shop("PENDING", "32", "3201", HarShopCreateStatus.PENDING, "S2"),
-                shop("NOID", "32", "3201", HarShopCreateStatus.SUCCESS, null),
-                shop("BLANKID", "32", "3201", HarShopCreateStatus.SUCCESS, "  "),
-                shop("NOSTATUS", "32", "3201", null, "S5"));
-        assertNull(pick(unusable, "32", "3201", "114.114.114.114"), "未建成的店不能拿去下单");
+                shop("FAILED", "320000", "320100", HarShopCreateStatus.FAILED, "S1"),
+                shop("PENDING", "320000", "320100", HarShopCreateStatus.PENDING, "S2"),
+                shop("NOID", "320000", "320100", HarShopCreateStatus.SUCCESS, null),
+                shop("BLANKID", "320000", "320100", HarShopCreateStatus.SUCCESS, "  "),
+                shop("NOSTATUS", "320000", "320100", null, "S5"));
+        assertNull(pick(unusable, "320000", "320100", "114.114.114.114"), "未建成的店不能拿去下单");
 
         List<HarShopEntity> withOneGood = new ArrayList<>(unusable);
-        withOneGood.add(ok("GOOD", "32", "3201"));
-        assertEquals("GOOD", pick(withOneGood, "32", "3201", "114.114.114.114").getThirdShopId());
+        withOneGood.add(ok("GOOD", "320000", "320100"));
+        assertEquals("GOOD", pick(withOneGood, "320000", "320100", "114.114.114.114").getThirdShopId());
     }
 
     @Test
     void pick_noCandidatesAtAll_picksNothing() {
-        assertNull(pick(List.of(), "32", "3201", "114.114.114.114"));
+        assertNull(pick(List.of(), "320000", "320100", "114.114.114.114"));
     }
 
     // ──────────────────────────── 同地区多店:按 IP 稳定散列 ────────────────────────────
 
     private static List<HarShopEntity> fourShopsInJiangsu() {
         return List.of(
-                ok("A", "32", "3201"),
-                ok("B", "32", "3201"),
-                ok("C", "32", "3201"),
-                ok("D", "32", "3201"));
+                ok("A", "320000", "320100"),
+                ok("B", "320000", "320100"),
+                ok("C", "320000", "320100"),
+                ok("D", "320000", "320100"));
     }
 
     /** 同一个顾客(同 IP)在缓存过期后重来一次,必须还是那一家 */
@@ -176,8 +176,8 @@ class HarShopRegionPickTest {
 
         for (int i = 0; i < 50; i++) {
             String seed = "10.0." + (i / 250) + "." + (i % 250);
-            assertEquals(pick(candidates, "32", "3201", seed).getThirdShopId(),
-                    pick(candidates, "32", "3201", seed).getThirdShopId(),
+            assertEquals(pick(candidates, "320000", "320100", seed).getThirdShopId(),
+                    pick(candidates, "320000", "320100", seed).getThirdShopId(),
                     "同一 IP 必须稳定落到同一家,seed=" + seed);
         }
     }
@@ -189,7 +189,7 @@ class HarShopRegionPickTest {
         Set<String> chosen = new HashSet<>();
 
         for (int i = 0; i < 400; i++) {
-            chosen.add(pick(candidates, "32", "3201", "10.0." + (i / 250) + "." + (i % 250)).getThirdShopId());
+            chosen.add(pick(candidates, "320000", "320100", "10.0." + (i / 250) + "." + (i % 250)).getThirdShopId());
         }
 
         assertEquals(Set.of("A", "B", "C", "D"), chosen, "400 个不同 IP 应把所有店都用上");
@@ -200,10 +200,10 @@ class HarShopRegionPickTest {
     void pick_blankSeedIsStillDeterministic() {
         List<HarShopEntity> candidates = fourShopsInJiangsu();
 
-        HarShopEntity first = pick(candidates, "32", "3201", null);
+        HarShopEntity first = pick(candidates, "320000", "320100", null);
         assertNotNull(first);
-        assertEquals(first.getThirdShopId(), pick(candidates, "32", "3201", null).getThirdShopId());
-        assertEquals(first.getThirdShopId(), pick(candidates, "32", "3201", "  ").getThirdShopId());
+        assertEquals(first.getThirdShopId(), pick(candidates, "320000", "320100", null).getThirdShopId());
+        assertEquals(first.getThirdShopId(), pick(candidates, "320000", "320100", "  ").getThirdShopId());
     }
 
     // ──────────────────────────── 查询本身 ────────────────────────────
@@ -219,7 +219,7 @@ class HarShopRegionPickTest {
         ArgumentCaptor<LambdaQueryWrapper<HarShopEntity>> captor =
                 ArgumentCaptor.forClass(LambdaQueryWrapper.class);
 
-        assertNull(service.pickForRegion("M001", "32", "3201", "114.114.114.114"));
+        assertNull(service.pickForRegion("M001", "320000", "320100", "114.114.114.114"));
 
         verify(shopMapper).selectList(captor.capture());
         String sql = captor.getValue().getSqlSegment();
@@ -229,7 +229,7 @@ class HarShopRegionPickTest {
     /** 商户号本身为空就别去查库了(空串会变成 merchant_code = '',查出一片空并静默拒收) */
     @Test
     void pickForRegion_blankMerchantCode_picksNothingWithoutQuerying() {
-        assertNull(service.pickForRegion("  ", "32", "3201", "114.114.114.114"));
+        assertNull(service.pickForRegion("  ", "320000", "320100", "114.114.114.114"));
 
         verify(shopMapper, org.mockito.Mockito.never()).selectList(any());
     }
@@ -238,10 +238,10 @@ class HarShopRegionPickTest {
     @Test
     void pickForRegion_picksFromWhatTheQueryReturned() {
         when(shopMapper.selectList(any())).thenReturn(List.of(
-                ok("NJ", "32", "3201"),
-                ok("HZ", "33", "3301")));
+                ok("NJ", "320000", "320100"),
+                ok("HZ", "330000", "330100")));
 
-        assertEquals("NJ", service.pickForRegion("M001", "32", "3201", "114.114.114.114").getThirdShopId());
+        assertEquals("NJ", service.pickForRegion("M001", "320000", "320100", "114.114.114.114").getThirdShopId());
     }
 
     // ──────────────── 下单按顾客归属挑店(开放 API,2026-10-09 改版:IP 优先、省码回退) ────────────────
@@ -261,10 +261,10 @@ class HarShopRegionPickTest {
     void pickForOrder_ipResolves_beatsTheProvinceCode() {
         when(ipRegionResolver.lookup("114.114.114.114")).thenReturn(Optional.of(ipGeoHunan("长沙")));
         when(shopMapper.selectList(any())).thenReturn(List.of(
-                ok("NJ", "32", "3201"),
-                ok("CS", "43", "4301")));
+                ok("NJ", "320000", "320100"),
+                ok("CS", "430000", "430100")));
 
-        assertEquals("CS", service.pickForOrder("M001", "32", "114.114.114.114", "BIZ-1").getThirdShopId(),
+        assertEquals("CS", service.pickForOrder("M001", "320000", "114.114.114.114", "BIZ-1").getThirdShopId(),
                 "省码是江苏、IP 在湖南 ⇒ 挑湖南的店");
     }
 
@@ -273,10 +273,10 @@ class HarShopRegionPickTest {
     void pickForOrder_ipCityUnknown_fallsBackToTheIpProvince() {
         when(ipRegionResolver.lookup("114.114.114.114")).thenReturn(Optional.of(ipGeoHunan("渝中區")));
         when(shopMapper.selectList(any())).thenReturn(List.of(
-                ok("CS", "43", "4301"),
-                ok("NJ", "32", "3201")));
+                ok("CS", "430000", "430100"),
+                ok("NJ", "320000", "320100")));
 
-        assertEquals("CS", service.pickForOrder("M001", "32", "114.114.114.114", "BIZ-1").getThirdShopId(),
+        assertEquals("CS", service.pickForOrder("M001", "320000", "114.114.114.114", "BIZ-1").getThirdShopId(),
                 "IP 的省里有店就不该回退到调用方传的省码");
     }
 
@@ -284,9 +284,9 @@ class HarShopRegionPickTest {
     @Test
     void pickForOrder_ipRegionHasNoShop_fallsBackToProvinceCode() {
         when(ipRegionResolver.lookup("114.114.114.114")).thenReturn(Optional.of(ipGeoHunan("长沙")));
-        when(shopMapper.selectList(any())).thenReturn(List.of(ok("NJ", "32", "3201")));
+        when(shopMapper.selectList(any())).thenReturn(List.of(ok("NJ", "320000", "320100")));
 
-        assertEquals("NJ", service.pickForOrder("M001", "32", "114.114.114.114", "BIZ-1").getThirdShopId());
+        assertEquals("NJ", service.pickForOrder("M001", "320000", "114.114.114.114", "BIZ-1").getThirdShopId());
     }
 
     /** 挑中的店没有 third_shop_id(脏数据)与「没挑到」同权 ⇒ 同样回退省码 */
@@ -294,19 +294,19 @@ class HarShopRegionPickTest {
     void pickForOrder_ipPickYieldsShopWithoutThirdShopId_fallsBackToProvinceCode() {
         when(ipRegionResolver.lookup("114.114.114.114")).thenReturn(Optional.of(ipGeoHunan("长沙")));
         when(shopMapper.selectList(any())).thenReturn(List.of(
-                shop(null, "43", "4301", HarShopCreateStatus.SUCCESS, "S-PLATFORM"),
-                ok("NJ", "32", "3201")));
+                shop(null, "430000", "430100", HarShopCreateStatus.SUCCESS, "S-PLATFORM"),
+                ok("NJ", "320000", "320100")));
 
-        assertEquals("NJ", service.pickForOrder("M001", "32", "114.114.114.114", "BIZ-1").getThirdShopId());
+        assertEquals("NJ", service.pickForOrder("M001", "320000", "114.114.114.114", "BIZ-1").getThirdShopId());
     }
 
     /** IP 解析不出(内网/非大陆/取数失败)⇒ 有省码就按省挑 —— 这正是「两个可以同时传」的用法 */
     @Test
     void pickForOrder_ipUnresolvable_fallsBackToProvinceCode() {
         when(ipRegionResolver.lookup("1.2.3.4")).thenReturn(Optional.empty());
-        when(shopMapper.selectList(any())).thenReturn(List.of(ok("NJ", "32", "3201")));
+        when(shopMapper.selectList(any())).thenReturn(List.of(ok("NJ", "320000", "320100")));
 
-        assertEquals("NJ", service.pickForOrder("M001", "32", "1.2.3.4", "BIZ-1").getThirdShopId());
+        assertEquals("NJ", service.pickForOrder("M001", "320000", "1.2.3.4", "BIZ-1").getThirdShopId());
     }
 
     /** 解析出了但省名不在目录里 ⇒ 同样回退省码(港澳台已被解析器在更上游挡掉) */
@@ -314,9 +314,9 @@ class HarShopRegionPickTest {
     void pickForOrder_ipRegionNameNotInCatalog_fallsBackToProvinceCode() {
         when(ipRegionResolver.lookup("1.2.3.4"))
                 .thenReturn(Optional.of(new HarIpRegionResolver.IpGeo("CN", "某某省", "XX", "某市")));
-        when(shopMapper.selectList(any())).thenReturn(List.of(ok("NJ", "32", "3201")));
+        when(shopMapper.selectList(any())).thenReturn(List.of(ok("NJ", "320000", "320100")));
 
-        assertEquals("NJ", service.pickForOrder("M001", "32", "1.2.3.4", "BIZ-1").getThirdShopId());
+        assertEquals("NJ", service.pickForOrder("M001", "320000", "1.2.3.4", "BIZ-1").getThirdShopId());
     }
 
     /** 只传 ip 且解析不出:是调用方的输入错误,响亮拒绝 —— 静默不带门店下单 = 地区意图悄悄落空 */
@@ -336,12 +336,12 @@ class HarShopRegionPickTest {
     @Test
     void pickForOrder_ipOnly_regionHasNoShop_400_namesTheIpRegion() {
         when(ipRegionResolver.lookup("114.114.114.114")).thenReturn(Optional.of(ipGeoHunan("长沙")));
-        when(shopMapper.selectList(any())).thenReturn(List.of(ok("NJ", "32", "3201")));
+        when(shopMapper.selectList(any())).thenReturn(List.of(ok("NJ", "320000", "320100")));
 
         BusinessException e = assertThrows(BusinessException.class,
                 () -> service.pickForOrder("M001", null, "114.114.114.114", "BIZ-1"));
 
-        assertEquals("该地区没有可用门店: 4301(省 43)", e.getMessage());
+        assertEquals("该地区没有可用门店: 430100(省 430000)", e.getMessage());
     }
 
     /** 两个都没传(上线路径由 create 的守卫挡住,这里防将来直调时抛 NPE) */
@@ -357,14 +357,14 @@ class HarShopRegionPickTest {
     @Test
     void pickForOrder_provinceOnly_blankSeed_spreadsAcrossCalls() {
         when(shopMapper.selectList(any())).thenReturn(List.of(
-                ok("A", "32", "3201"),
-                ok("B", "32", "3201"),
-                ok("C", "32", "3205"),
-                ok("D", "32", "3205")));
+                ok("A", "320000", "320100"),
+                ok("B", "320000", "320100"),
+                ok("C", "320000", "320500"),
+                ok("D", "320000", "320500")));
 
         Set<String> chosen = new HashSet<>();
         for (int i = 0; i < 60; i++) {
-            chosen.add(service.pickForOrder("M001", "32", null, null).getThirdShopId());
+            chosen.add(service.pickForOrder("M001", "320000", null, null).getThirdShopId());
         }
 
         assertTrue(chosen.size() > 1, "没传 out_biz_no 时也必须是随机挑而不是固定第一家,实际: " + chosen);
@@ -375,24 +375,24 @@ class HarShopRegionPickTest {
     @Test
     void pickForOrder_sameOutBizNo_isStable() {
         when(shopMapper.selectList(any())).thenReturn(List.of(
-                ok("A", "32", "3201"),
-                ok("B", "32", "3201"),
-                ok("C", "32", "3205"),
-                ok("D", "32", "3205")));
+                ok("A", "320000", "320100"),
+                ok("B", "320000", "320100"),
+                ok("C", "320000", "320500"),
+                ok("D", "320000", "320500")));
 
-        String first = service.pickForOrder("M001", "32", null, "BIZ-1").getThirdShopId();
+        String first = service.pickForOrder("M001", "320000", null, "BIZ-1").getThirdShopId();
         for (int i = 0; i < 10; i++) {
-            assertEquals(first, service.pickForOrder("M001", "32", null, "BIZ-1").getThirdShopId());
+            assertEquals(first, service.pickForOrder("M001", "320000", null, "BIZ-1").getThirdShopId());
         }
     }
 
     @Test
     void pickForOrder_provinceOnly_stillPicksWithinProvince() {
         when(shopMapper.selectList(any())).thenReturn(List.of(
-                ok("SZ", "32", "3205"),
-                ok("HZ", "33", "3301")));
+                ok("SZ", "320000", "320500"),
+                ok("HZ", "330000", "330100")));
 
-        HarShopEntity picked = service.pickForOrder("M001", "32", null, "BIZ-1");
+        HarShopEntity picked = service.pickForOrder("M001", "320000", null, "BIZ-1");
 
         assertEquals("SZ", picked.getThirdShopId(), "不给 IP ⇒ 按全省挑,异省的店绝不越界");
     }
@@ -400,22 +400,22 @@ class HarShopRegionPickTest {
     @Test
     void pickForOrder_unknownProvinceCode_400_sameMessageAsShopCreate() {
         BusinessException e = assertThrows(BusinessException.class,
-                () -> service.pickForOrder("M001", "99", null, "BIZ-1"));
+                () -> service.pickForOrder("M001", "999999", null, "BIZ-1"));
 
         assertEquals(400, e.getCode());
-        assertEquals("省份编码不存在: 99", e.getMessage(),
+        assertEquals("省份编码不存在: 999999", e.getMessage(),
                 "与门店创建那条必须一字不差 —— 开放文档 10.1/10.2 引用的是同一句话");
         verify(shopMapper, org.mockito.Mockito.never()).selectList(any());
     }
 
     @Test
     void pickForOrder_noUsableShopInProvince_400_namesTheProvince() {
-        when(shopMapper.selectList(any())).thenReturn(List.of(ok("HZ", "33", "3301")));
+        when(shopMapper.selectList(any())).thenReturn(List.of(ok("HZ", "330000", "330100")));
 
         BusinessException e = assertThrows(BusinessException.class,
-                () -> service.pickForOrder("M001", "32", null, "BIZ-1"));
+                () -> service.pickForOrder("M001", "320000", null, "BIZ-1"));
 
-        assertEquals("该地区没有可用门店: 省 32", e.getMessage());
+        assertEquals("该地区没有可用门店: 省 320000", e.getMessage());
     }
 
     /**
@@ -426,11 +426,11 @@ class HarShopRegionPickTest {
     @Test
     void pickForOrder_pickedShopWithoutThirdShopId_400() {
         when(shopMapper.selectList(any())).thenReturn(List.of(
-                shop(null, "32", "3201", HarShopCreateStatus.SUCCESS, "S-PLATFORM")));
+                shop(null, "320000", "320100", HarShopCreateStatus.SUCCESS, "S-PLATFORM")));
 
         BusinessException e = assertThrows(BusinessException.class,
-                () -> service.pickForOrder("M001", "32", null, "BIZ-1"));
+                () -> service.pickForOrder("M001", "320000", null, "BIZ-1"));
 
-        assertEquals("该地区没有可用门店: 省 32", e.getMessage());
+        assertEquals("该地区没有可用门店: 省 320000", e.getMessage());
     }
 }

+ 11 - 10
java/src/test/java/com/payment/platform/module/payment/har/service/HarShopServiceTest.java

@@ -62,8 +62,9 @@ class HarShopServiceTest {
         dto.setThirdShopId("SHOP001");
         dto.setShopName("测试门店");
         // 省市编码自 2026-10-08 起上游必填,本地也会对目录校验 —— 缺了走不到调上游那一步
-        dto.setProvinceCode("32");
-        dto.setCityCode("3201");
+        // (2026-10-09 起是 GB/T 2260 六位码,形状由 HarRegionCatalogTest 守卫)
+        dto.setProvinceCode("320000");
+        dto.setCityCode("320100");
         return dto;
     }
 
@@ -243,17 +244,17 @@ class HarShopServiceTest {
 
         ArgumentCaptor<HarShopCreateRequest> captor = ArgumentCaptor.forClass(HarShopCreateRequest.class);
         verify(harClient).createShop(captor.capture());
-        assertEquals("32", captor.getValue().getProvinceCode());
-        assertEquals("3201", captor.getValue().getCityCode());
+        assertEquals("320000", captor.getValue().getProvinceCode());
+        assertEquals("320100", captor.getValue().getCityCode());
 
         String biz = HarClient.toBizContent(captor.getValue());
-        assertTrue(biz.contains("\"province_code\":\"32\""), "实际报文: " + biz);
-        assertTrue(biz.contains("\"city_code\":\"3201\""), "实际报文: " + biz);
+        assertTrue(biz.contains("\"province_code\":\"320000\""), "实际报文: " + biz);
+        assertTrue(biz.contains("\"city_code\":\"320100\""), "实际报文: " + biz);
 
         ArgumentCaptor<HarShopEntity> row = ArgumentCaptor.forClass(HarShopEntity.class);
         verify(shopMapper).insert(row.capture());
-        assertEquals("32", row.getValue().getProvinceCode());
-        assertEquals("3201", row.getValue().getCityCode());
+        assertEquals("320000", row.getValue().getProvinceCode());
+        assertEquals("320100", row.getValue().getCityCode());
     }
 
     /**
@@ -266,7 +267,7 @@ class HarShopServiceTest {
     @Test
     void create_rejectsCityCodeThatIsNotInTheGivenProvince() {
         HarShopCreateDTO dto = dto();
-        dto.setCityCode("3301");   // 杭州,挂在江苏名下
+        dto.setCityCode("330100");   // 杭州,挂在江苏名下
 
         BusinessException e = assertThrows(BusinessException.class, () -> service.create(dto, "E1"));
 
@@ -283,7 +284,7 @@ class HarShopServiceTest {
     @Test
     void create_rejectsUnknownProvinceCode() {
         HarShopCreateDTO dto = dto();
-        dto.setProvinceCode("99");
+        dto.setProvinceCode("999999");
 
         BusinessException e = assertThrows(BusinessException.class, () -> service.create(dto, "E1"));
 

+ 49 - 45
java/src/test/java/com/payment/platform/module/payment/har/util/HarRegionCatalogTest.java

@@ -36,13 +36,14 @@ class HarRegionCatalogTest {
     // ──────────────────────────── 快照完整性 ────────────────────────────
 
     @Test
-    void provinces_snapshotIs31_uniqueTwoDigitCodes() {
+    void provinces_snapshotIs31_uniqueSixDigitCodes() {
         List<Province> ps = catalog.provinces();
 
         assertEquals(PROVINCE_COUNT, ps.size(), "省级条数变了 ⇒ 快照被换过,下游匹配的分支要一并复看");
         assertEquals(PROVINCE_COUNT, ps.stream().map(Province::code).distinct().count(), "省级编码有重复");
         assertEquals(PROVINCE_COUNT, ps.stream().map(Province::name).distinct().count(), "省级名称有重复");
-        assertTrue(ps.stream().allMatch(p -> p.code().matches("\\d{2}")), "省级编码必须是 2 位数字");
+        assertTrue(ps.stream().allMatch(p -> p.code().matches("\\d{2}0000")),
+                "省级编码必须是 GB/T 2260 六位(2 位省码 + 0000)—— 上游对照表(2026-10-09 截图)就是六位");
         assertTrue(ps.stream().allMatch(p -> !p.name().isBlank()), "省级名称不能为空");
     }
 
@@ -54,9 +55,10 @@ class HarRegionCatalogTest {
 
         assertEquals(CITY_COUNT, all.size(), "地级条数变了 ⇒ 快照被换过");
         assertEquals(CITY_COUNT, all.stream().map(City::code).distinct().count(), "地级编码有重复");
-        assertTrue(all.stream().allMatch(c -> c.code().matches("\\d{4}")), "地级编码必须是 4 位数字");
-        assertTrue(all.stream().allMatch(c -> c.code().startsWith(c.provinceCode())),
-                "地级编码必须以省码开头 —— 前缀断裂会让「按省回退」捞到别的省的门店");
+        assertTrue(all.stream().allMatch(c -> c.code().matches("\\d{4}00")),
+                "地级编码必须是 GB/T 2260 六位(4 位市码 + 00)");
+        assertTrue(all.stream().allMatch(c -> c.code().startsWith(c.provinceCode().substring(0, 2))),
+                "地级编码前两位必须是省码的前两位 —— 前缀断裂会让「按省回退」捞到别的省的门店");
         assertTrue(all.stream().allMatch(c -> catalog.provinces().stream()
                         .anyMatch(p -> p.code().equals(c.provinceCode()))),
                 "地级指向了不存在的省");
@@ -78,7 +80,7 @@ class HarRegionCatalogTest {
     }
 
     /**
-     * 直辖市的城市下拉不能出现「市辖区」(GB/T 2260 里 1101/1201/3101/5001 的名字),
+     * 直辖市的城市下拉不能出现「市辖区」(GB/T 2260 里 110100/120100/310100/500100 的名字),
      * 顾客与商户读到的应该是省名。
      */
     @Test
@@ -148,17 +150,17 @@ class HarRegionCatalogTest {
     /** 输入全部抄自 2026-10-08 的 ipwho.is 实测回包(lang=zh-CN 的 region 字段) */
     @Test
     void matchProvince_handlesTraditionalAndSuffixVariants() {
-        assertEquals("54", provinceCode("西藏自治區"), "实测回包里的繁体「區」");
-        assertEquals("54", provinceCode("西藏自治区"));
-        assertEquals("65", provinceCode("新疆维吾尔自治区"));
-        assertEquals("45", provinceCode("广西壮族自治区"));
-        assertEquals("64", provinceCode("宁夏回族自治区"));
-        assertEquals("15", provinceCode("内蒙古自治区"));
-        assertEquals("15", provinceCode("内蒙古"));
-        assertEquals("11", provinceCode("北京市"));
-        assertEquals("11", provinceCode("北京"));
-        assertEquals("32", provinceCode("江苏省"));
-        assertEquals("43", provinceCode("湖南省"), "实测回包(118.248.202.107)");
+        assertEquals("540000", provinceCode("西藏自治區"), "实测回包里的繁体「區」");
+        assertEquals("540000", provinceCode("西藏自治区"));
+        assertEquals("650000", provinceCode("新疆维吾尔自治区"));
+        assertEquals("450000", provinceCode("广西壮族自治区"));
+        assertEquals("640000", provinceCode("宁夏回族自治区"));
+        assertEquals("150000", provinceCode("内蒙古自治区"));
+        assertEquals("150000", provinceCode("内蒙古"));
+        assertEquals("110000", provinceCode("北京市"));
+        assertEquals("110000", provinceCode("北京"));
+        assertEquals("320000", provinceCode("江苏省"));
+        assertEquals("430000", provinceCode("湖南省"), "实测回包(118.248.202.107)");
     }
 
     @Test
@@ -173,37 +175,37 @@ class HarRegionCatalogTest {
 
     @Test
     void matchCity_isScopedToTheGivenProvince() {
-        assertEquals("3201", cityCode("32", "南京"));
-        assertEquals("3201", cityCode("32", "南京市"));
-        assertEquals("6523", cityCode("65", "昌吉回族自治州"));
-        assertEquals("5401", cityCode("54", "拉萨"));
-        assertEquals("5401", cityCode("54", "拉薩"), "同一条实测回包里 city 是简体,但别的字段出现过繁体");
-        assertEquals("3202", cityCode("32", "無錫市"), "繁体整串也要能命中");
+        assertEquals("320100", cityCode("320000", "南京"));
+        assertEquals("320100", cityCode("320000", "南京市"));
+        assertEquals("652300", cityCode("650000", "昌吉回族自治州"));
+        assertEquals("540100", cityCode("540000", "拉萨"));
+        assertEquals("540100", cityCode("540000", "拉薩"), "同一条实测回包里 city 是简体,但别的字段出现过繁体");
+        assertEquals("320200", cityCode("320000", "無錫市"), "繁体整串也要能命中");
         // 实测回包(118.248.202.107):省名带「省」、市名不带「市」——两侧都要能命中同一个地级
-        assertEquals("4301", cityCode("43", "长沙"), "实测回包 city 不带后缀");
-        assertEquals("4301", cityCode("43", "长沙市"));
+        assertEquals("430100", cityCode("430000", "长沙"), "实测回包 city 不带后缀");
+        assertEquals("430100", cityCode("430000", "长沙市"));
     }
 
     @Test
     void matchCity_districtLevelOrForeignName_returnsEmpty() {
-        assertTrue(catalog.matchCity("50", "渝中區").isEmpty(), "实测回包里重庆市回的是区级名");
-        assertTrue(catalog.matchCity("11", "").isEmpty());
-        assertTrue(catalog.matchCity("11", null).isEmpty());
-        assertTrue(catalog.matchCity("32", "Tokyo").isEmpty());
-        assertTrue(catalog.matchCity("99", "南京").isEmpty(), "省码不存在");
+        assertTrue(catalog.matchCity("500000", "渝中區").isEmpty(), "实测回包里重庆市回的是区级名");
+        assertTrue(catalog.matchCity("110000", "").isEmpty());
+        assertTrue(catalog.matchCity("110000", null).isEmpty());
+        assertTrue(catalog.matchCity("320000", "Tokyo").isEmpty());
+        assertTrue(catalog.matchCity("999999", "南京").isEmpty(), "省码不存在");
     }
 
     /**
-     * 直辖市的 1101/1201/3101 展示成省名,于是「北京市 + 北京」能命中市一级。
+     * 直辖市的 110100/120100/310100 展示成省名,于是「北京市 + 北京」能命中市一级。
      *
      * <p>★ 重庆(50)**有意不在**这条用例里:实测它的回包给的是区级名(渝中區),
      * 走的是「市级未命中 ⇒ 回退全省」那一步;这里不写我没量到过的行为。
      */
     @Test
     void municipalities_matchAtCityLevelViaTheProvinceName() {
-        assertEquals("1101", cityCode("11", "北京"));
-        assertEquals("3101", cityCode("31", "上海"));
-        assertEquals("1201", cityCode("12", "天津"));
+        assertEquals("110100", cityCode("110000", "北京"));
+        assertEquals("310100", cityCode("310000", "上海"));
+        assertEquals("120100", cityCode("120000", "天津"));
     }
 
     // ──────────────────────────── 按编码查(门店创建校验用) ────────────────────────────
@@ -218,18 +220,20 @@ class HarRegionCatalogTest {
     @Test
     void cityByCode_isScopedToTheGivenProvince() {
         // 与按名匹配互相对照:两条路必须指向同一条记录(同一条自证不算数)
-        assertEquals(catalog.matchCity("32", "南京").orElseThrow(),
-                catalog.cityByCode("32", "3201").orElseThrow());
-
-        assertTrue(catalog.cityByCode("32", "3301").isEmpty(), "3301 是浙江的市码,不该挂在江苏名下");
-        assertTrue(catalog.cityByCode("99", "3201").isEmpty(), "省码不存在");
-        assertTrue(catalog.cityByCode("32", "320").isEmpty(), "位数不对");
-        assertTrue(catalog.cityByCode("32", "32").isEmpty());
-        assertTrue(catalog.cityByCode("32", null).isEmpty());
-        assertTrue(catalog.cityByCode(null, "3201").isEmpty());
-        assertTrue(catalog.cityByCode("32", "  ").isEmpty());
+        assertEquals(catalog.matchCity("320000", "南京").orElseThrow(),
+                catalog.cityByCode("320000", "320100").orElseThrow());
+
+        assertTrue(catalog.cityByCode("320000", "330100").isEmpty(), "330100 是浙江的市码,不该挂在江苏名下");
+        assertTrue(catalog.cityByCode("999999", "320100").isEmpty(), "省码不存在");
+        assertTrue(catalog.cityByCode("320000", "3201").isEmpty(), "旧四位短码已作废(2026-10-09 起六位)");
+        assertTrue(catalog.cityByCode("32", "320100").isEmpty(), "旧两位省码已作废");
+        assertTrue(catalog.cityByCode("320000", "32010").isEmpty(), "位数不对");
+        assertTrue(catalog.cityByCode("320000", "320000").isEmpty());
+        assertTrue(catalog.cityByCode("320000", null).isEmpty());
+        assertTrue(catalog.cityByCode(null, "320100").isEmpty());
+        assertTrue(catalog.cityByCode("320000", "  ").isEmpty());
         // 正控:同一个量具对**该在**的码必须命中,否则上面那串空可能只是它永远查不到东西
-        assertTrue(catalog.cityByCode("32", "3201").isPresent());
+        assertTrue(catalog.cityByCode("320000", "320100").isPresent());
     }
 
     /** 完备性:数据集里每一个市码都要按码查得到,且查回来还是它自己 */