13 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 6350 | 行政区划主数据与三级联动 | admin | lc(GIT) | 新增接口 | deployed | verified | pending | 2026-08-26 | 主工单 #6350 及补充工单 #6384、#6389、#6395、#6399、#6409 的六个 PR 均已合并 dev-v3。精确提交 b62054035a139e3c689179ef75e2d052d08dde52 已由 Deploy Panel 任务 52771074(hl-user-service)和 b1db24b2(hl-gateway)部署 TEST;真实管理员经 Gateway 完成未认证、读取、失败零写入、创建、CAS 更新、数据库和 Redis 一致性验收,临时数据、缓存快照及登录会话均已恢复。管理端尚待接入新增接口。 | 2026-08-26 | dev-v3 |
行政区划主数据与三级联动
管理后台新增可版本化的行政区划主数据接口,统一提供省、市、县三级联动、任意节点父链回显,以及受权限、有效期、层级和并发版本保护的创建与更新能力。
服务:
hl-user-service,统一经 Gateway/admin/region/**访问 Issue:#6350、#6384、#6389、#6395、#6399、#6409 PR:#6359、#6387、#6393、#6398、#6402、#6412 影响范围:管理后台行政区划维护、三级选择器和历史值父链回显
关键约定
- 所有响应中的行政区划 ID 均为 JSON 字符串,包括
data、parentId、defaultId、selectedId、provinceId、prefectureId、countyId和节点id;没有父级或对应层级时保持null。 - 当前写入边界为省、市、县三级,层级代码依次为
PROVINCE、PREFECTURE、COUNTY;服务端根据父链计算depth,请求不能直接指定深度。 ADMINISTRATIVE表示可作为业务行政区的节点;AGGREGATION只用于导航分组,必须使用HL_GROUP、navigable=true、selectable=false。- 有效期使用左闭右开区间
[validFrom, validTo);validTo=null表示持续有效。 - 读接口只要求真实管理员登录;创建还要求
system:region:create,更新要求system:region:update。 - 统一响应为
Result<T>。业务失败可能仍是 HTTP 200,调用方必须同时检查code、success、message和data。
变更接口清单
| 方法 | 路径 | 权限 | 用途 |
|---|---|---|---|
| POST | /admin/region |
system:region:create |
创建行政区或导航聚合节点 |
| PUT | /admin/region/{id} |
system:region:update |
使用 rowVersion 更新可变字段 |
| GET | /admin/region/children |
管理员登录 | 查询根层或指定父节点的当前有效直接子节点 |
| GET | /admin/region/{id}/path |
管理员登录 | 返回任意节点从省级根开始的完整父链 |
1. 单级联动列表
GET /admin/region/children?parentId=1&selectedId=35
Authorization: Bearer <管理员令牌>
查询省级根列表时省略 parentId。selectedId 只有在本次 items 中存在时才作为 defaultId;否则回退为排序后的首项,空列表返回 null。
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"parentId": "1",
"items": [
{
"id": "35",
"parentId": "1",
"codeStandard": "GB/T 2260",
"regionCode": "110100",
"levelCode": "PREFECTURE",
"depth": 2,
"name": "北京市",
"shortName": null,
"nodeKind": "ADMINISTRATIVE",
"navigable": true,
"selectable": true,
"hasChildren": true,
"currentlyEffective": true,
"status": "ACTIVE",
"validFrom": "2025-12-31",
"validTo": null,
"sortOrder": 100,
"rowVersion": 0
}
],
"defaultId": "35"
}
}
仅返回中国业务当日有效且状态为 ACTIVE 的直接子节点,排序稳定为 sortOrder、regionCode、id。停用或历史节点不会进入联动列表,但仍可通过父链接口回显。
2. 父链回显
GET /admin/region/376/path
Authorization: Bearer <管理员令牌>
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"selectedId": "376",
"provinceId": "1",
"prefectureId": "35",
"countyId": "376",
"path": [
{
"id": "1",
"parentId": null,
"levelCode": "PROVINCE",
"depth": 1,
"name": "北京市",
"nodeKind": "ADMINISTRATIVE",
"currentlyEffective": true
},
{
"id": "35",
"parentId": "1",
"levelCode": "PREFECTURE",
"depth": 2,
"name": "北京市",
"nodeKind": "ADMINISTRATIVE",
"currentlyEffective": true
},
{
"id": "376",
"parentId": "35",
"levelCode": "COUNTY",
"depth": 3,
"name": "东城区",
"nodeKind": "ADMINISTRATIVE",
"currentlyEffective": true
}
]
}
}
目标为省级或地级时,尚未到达的快捷层级字段返回 null。历史停用节点可返回,但节点自身的 currentlyEffective=false,调用方不能把它重新放入当前可选列表。父链存在孤儿、环、越级或超过当前安全深度时,接口失败且不返回部分路径。
3. 创建节点
POST /admin/region
Authorization: Bearer <具有 system:region:create 的管理员令牌>
Content-Type: application/json
创建一个县级业务行政区示例:
{
"codeStandard": "HL_CUSTOM",
"regionCode": "DEMO-COUNTY-001",
"parentId": "35",
"levelCode": "COUNTY",
"nodeKind": "ADMINISTRATIVE",
"name": "示例业务区",
"shortName": "示例区",
"navigable": true,
"selectable": true,
"validFrom": "2026-08-26",
"validTo": null,
"status": "ACTIVE",
"sortOrder": 10000,
"sourceVersion": "ADMIN-20260826",
"sourceRef": "工单或数据批次定位",
"extension": {
"businessNote": "示例扩展信息"
}
}
成功响应的 data 是字符串 ID:
{
"code": 200,
"message": "成功",
"success": true,
"data": "2090000000000000001"
}
extension 只能是 JSON 对象;历史入参名 extJson 仅作为请求别名兼容,响应统一使用 extension。服务端会记录来源版本、来源定位、操作人和来源校验值,并在事务提交后刷新行政区划独立缓存。
4. 更新节点
PUT /admin/region/2090000000000000001
Authorization: Bearer <具有 system:region:update 的管理员令牌>
Content-Type: application/json
{
"parentId": "35",
"levelCode": "COUNTY",
"name": "示例业务区(更新)",
"shortName": "示例区",
"navigable": true,
"selectable": true,
"validFrom": "2026-08-26",
"validTo": null,
"status": "ACTIVE",
"sortOrder": 10001,
"extension": {
"businessNote": "名称已更新"
},
"rowVersion": 0
}
{
"code": 200,
"message": "行政区划更新成功",
"success": true,
"data": null
}
更新采用完整可变字段加 rowVersion 的 CAS 语义。成功后版本号递增;并发版本过期返回 210807,不会覆盖另一位管理员的提交。编码标准、地区编码、节点类型和来源证据不可通过更新接口改写。
已到生效日的 GB/T 2260 法定节点不能原位修改历史属性;只能保持其他字段不变,将旧版本关闭,再创建不重叠的新版本。有子节点的分支不能直接移动或改变层级,父节点的状态、导航能力和有效期必须持续覆盖当前或未来仍会生效的子版本。
字段说明
| 字段 | JSON 类型 | 说明 |
|---|---|---|
id、parentId |
string / null | 行政区划节点与父节点 ID;响应始终为字符串 |
codeStandard |
string | GB/T 2260、HL_CUSTOM 或聚合节点专用 HL_GROUP |
regionCode |
string | 同一编码标准内的业务标识;GB/T 2260 必须为六位数字 |
levelCode |
string | 当前三级固定为 PROVINCE、PREFECTURE、COUNTY |
nodeKind |
string | ADMINISTRATIVE 或 AGGREGATION |
navigable |
boolean | 是否可继续逐级导航 |
selectable |
boolean | 是否可作为最终业务值 |
hasChildren |
boolean | 是否存在当前有效、启用的直接子节点 |
currentlyEffective |
boolean | 节点在中国业务当日是否处于有效时间窗且为 ACTIVE |
validFrom、validTo |
string / null | yyyy-MM-dd;结束日为开区间 |
status |
string | ACTIVE 或 INACTIVE |
rowVersion |
integer | 更新 CAS 版本;创建后从 0 开始 |
业务错误码
| code | message | 典型场景 |
|---|---|---|
210801 |
行政区划节点不存在 | 查询不存在的父级或父链目标 |
210802 |
无行政区划维护权限 | 创建或更新缺少细粒度权限 |
210803 |
行政区划父节点不合法 | 父级不可导航或有效期不能覆盖子级 |
210804 |
行政区划层级超过当前允许深度 | 写入超过当前三级边界 |
210805 |
行政区划父链存在循环 | 自引用或移动后形成祖先环 |
210806 |
行政区划当前编码已存在 | 当前唯一键冲突 |
210807 |
行政区划已被其他操作更新,请刷新后重试 | rowVersion 过期 |
210808 |
行政区划有效期不合法 | validTo 不晚于 validFrom |
210809 |
该行政区划编码标准为系统保留值 | 新写入继续使用旧 HL_INTERNAL |
210810 |
存在子节点的行政区划不能移动或变更层级 | 分支移动或改层级 |
210811 |
行政区划父链数据不完整 | 孤儿、越级、环或异常终止 |
210812 |
行政区划扩展字段必须是合法 JSON | extension 不是 JSON 对象 |
210813 |
行政区划层级代码与父链深度不一致 | 省市县代码与派生深度不匹配 |
210814 |
行政区划更新与现有子节点状态或有效期冲突 | 父级提前停用、取消导航或缩短窗口 |
210815 |
行政区划编码的版本有效期与既有数据重叠 | 同编码版本时间窗重叠 |
210816 |
行政区划节点类型或编码命名空间不合法 | 聚合节点类型、命名空间或可选性组合错误 |
210817 |
已生效法定行政区划只能关闭旧版本后新增 | 原位改写生效中的法定节点 |
210818 |
行政区划数据来源信息不完整 | 缺少来源版本或定位 |
210819 |
法定行政区划代码必须为六位数字 | GB/T 2260 新版本编码格式错误 |
参数格式错误继续使用统一参数错误响应,例如未登录返回业务码 401,parentId=0 或空创建请求返回业务码 400。
兼容与接入建议
- 三级选择器首次加载调用不带
parentId的children;选择省、市后分别以当前 ID 继续查询下一层。 - 编辑历史数据时先调用
/{id}/path得到完整选中链,再逐层加载children;不要根据行政区编码截位推断父子关系。 AGGREGATION节点可能可导航但不可选,页面应分别使用navigable和selectable,不要只根据hasChildren判断。- 客户端必须把响应 ID 当字符串保存和比较;请求路径与查询参数可以继续发送十进制字符串。
- 旧行政区划缓存中的数字 ID 与新缓存中的字符串 ID 均可被后端读取,滚动部署期间无需调用方切换缓存版本。
验证证据
- PR #6359、#6387、#6393、#6398、#6402、#6412 均已合并
dev-v3,六个合并提交都包含在 TEST 精确提交b62054035a139e3c689179ef75e2d052d08dde52中。 - Deploy Panel API 任务
52771074部署hl-user-service,任务b1db24b2部署hl-gateway;两项脚本均为/opt/hulalv/scripts/deploy-backend.sh,终态success、退出码 0,双实例、Nacos 健康注册、滚动采样与部署窗口日志检查通过。 - 真实 TEST 管理员经 Gateway 验证未认证返回 401、根级/子级联动、父链回显、失败零写入、创建字符串 ID、数据库落行、Redis 回填、CAS 更新和更新后缓存一致性。
- 验收创建的唯一测试行已精确删除;数据库总量与测试命名空间恢复基线,四个 Redis 快照按原值和绝对过期时间恢复,缓存锁释放,登录会话注销。操作审计按系统设计保留。
撤回
代码撤回需回退上述六个 PR 并按依赖逆序重新部署 hl-gateway 与 hl-user-service;调用方停止访问 /admin/region/**,并经 Gateway 验证新增路由已不可达、既有 User 接口正常。数据库迁移已经在 TEST 应用,回退代码时保留行政区划表和权限元数据,不执行降版 DDL;Redis 仅在确需重建时精确清理 hl:user:region:v1: 命名空间,禁止触碰登录及其他业务缓存。无 MQ 或跨服务写入需要补偿。