diff --git a/docs/04-字段与接口映射.md b/docs/04-字段与接口映射.md index d148cdd..b27aaaa 100644 --- a/docs/04-字段与接口映射.md +++ b/docs/04-字段与接口映射.md @@ -237,3 +237,16 @@ checkId = 截断20位( 编码( 源系统标识 | dataType | 源记录主键 [ | | 药品目录查询 | `YPML00001` | 未做真实联调 | T-101 已完成客户端封装与单测;后续有药品关键字/拼音码时补样本 | > T-103 字典缓存可以先依赖网格、责任医生、机构三类真实验证结果。药品目录与健康档案创建闭环无直接依赖,不阻塞 T-103/T-206。 +--- + +## 10. JKDA 创建/更新请求契约(T-206 本地基线) + +T-206 已建立本地契约与客户端方法,真实写入验收暂未执行,原因见 `progress.md` 对应记录。 + +- `JKDA00001`:`/osi/api/jkda/create` → `osi.CreateHealthRecord` +- `JKDA00003`:`/osi/api/jkda/update` → `osi.UpdateHealthRecord` +- 请求仍走统一 `uploadinfo`,但创建/更新节点不是查询式裸 `baseInfo`,而是:`baseInfo`、`manageInfo`、`healthRecord`、`pastHistory`、`jwsjb`、`jwsss`、`jwsws`、`jwssx`、`familyMiddle` 同级。 +- 创建请求侧门牌号字段使用 `addressNumber`;查询响应侧仍以实测错拼 `adressNumber` 建模,二者不能混用。 +- `mapping.BuildHealthRecordCreate` 负责把 `MappedHealthRecord` 组装成 `contract.HealthRecordCreate`,保留确定性 `checkId`,并由 `manaDoctorId/manaUnitId` 派生 `createUser/createUnit`。 + +真实 create/update 联调完成后,需要在本节补回平台返回样本、`phrId` 字段确认、同一 `checkId` 重复 create 的幂等语义。 diff --git a/docs/current-state.md b/docs/current-state.md index a03c5b6..3e0c606 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -6,14 +6,14 @@ ## 当前快照 - 日期:2026-07-07 -- 阶段:**Phase D 字典缓存完成**;T-001~T-006、T-201、T-203、T-102、T-202、T-205、T-101、T-103 已验收,下一步进入 T-206 健康档案 Create/Update -- 技术栈:Go 1.24 单二进制;`main.go -mode server|deliver`;配置读取使用 viper,支持环境变量覆盖;OSI 客户端已具备签名、信封、传输、基础判码、JKDA00002 Find、JKDA00005 FindRqbj,以及 WGDZ/ZRYS/YPML/CXJG 四个公开查询薄封装 -- 生产代码:已有 `main.go`、`config/`、`contract/envelope.go`、`contract/jkda.go`、`osi/` 薄客户端、`cache/dictionary.go`、`verify_jkda.go`、`go.mod`/`go.sum`;`mapping/dict.go`、`mapping/health_record.go`、`mapping/checkid.go` 已建立映射纯函数、码表基线和主数据反查接入点;`pipeline/` 等业务模块仍待后续任务建立 -- 联调现实:**JKDA00002 个人档案查询已用 Go 侧真实请求打通**,返回 `code="01" message="操作成功" data_count=1`;**公开查询 WGDZ00001/ZRYS00001/CXJG00002 已用真实档案主数据验证通过**,均返回 `code="01"` 且数组非空;药品目录 YPML00001 已完成客户端封装和单测,尚未做真实药品关键字样本验证 -- 测试:`go test ./...` 通过;当前测试覆盖 mode 解析、配置加载与环境变量覆盖、MD5 签名、请求头组装、JSON POST 传输、头名大小写保留、identity 响应编码声明、超时配置、SOCKS5 代理地址校验、信封结构、serviceId 路由、成功/重试判码、Client.Call 请求与响应解析、JKDA00002 查询响应契约、JKDA00002 Find、JKDA00005 FindRqbj、公开查询四接口、字典缓存快照与持久化失败不阻断、映射码表双向查找、民族 01~56 完整性、健康档案映射必填/码表校验、主数据名称反查、checkId 确定性、docx/联调风格映射样本基线、JKDA00002 验证入口 +- 阶段:**Phase D 创建/更新代码基线完成,真实写入验收受阻**;T-001~T-006、T-201、T-203、T-102、T-202、T-205、T-101、T-103 已验收;T-206 已完成本地契约/客户端/映射组装代码,但未执行真实 `JKDA00001` create +- 技术栈:Go 1.24 单二进制;`main.go -mode server|deliver`;配置读取使用 viper,支持环境变量覆盖;OSI 客户端已具备签名、信封、传输、基础判码、JKDA00002 Find、JKDA00005 FindRqbj、JKDA00001 Create、JKDA00003 Update,以及 WGDZ/ZRYS/YPML/CXJG 四个公开查询薄封装 +- 生产代码:已有 `main.go`、`config/`、`contract/envelope.go`、`contract/jkda.go`、`osi/` 薄客户端、`cache/dictionary.go`、`verify_jkda.go`、`go.mod`/`go.sum`;`mapping/dict.go`、`mapping/health_record.go`、`mapping/checkid.go` 已建立映射纯函数、码表基线、主数据反查接入点和创建请求组装;`pipeline/` 等业务模块仍待后续任务建立 +- 联调现实:**JKDA00002 个人档案查询已用 Go 侧真实请求打通**,返回 `code="01" message="操作成功" data_count=1`;**公开查询 WGDZ00001/ZRYS00001/CXJG00002 已用真实档案主数据验证通过**,均返回 `code="01"` 且数组非空;药品目录 YPML00001 已完成客户端封装和单测,尚未做真实药品关键字样本验证;**JKDA00001 create 未跑真实请求**,避免在没有安全测试居民/写入授权时污染平台档案 +- 测试:`go test ./...` 通过;当前测试覆盖 mode 解析、配置加载与环境变量覆盖、MD5 签名、请求头组装、JSON POST 传输、头名大小写保留、identity 响应编码声明、超时配置、SOCKS5 代理地址校验、信封结构、serviceId 路由、成功/重试判码、Client.Call 请求与响应解析、JKDA00002 查询响应契约、JKDA00001/00003 创建更新请求契约与客户端方法、JKDA00002 Find、JKDA00005 FindRqbj、公开查询四接口、字典缓存快照与持久化失败不阻断、映射码表双向查找、民族 01~56 完整性、健康档案映射必填/码表校验、主数据名称反查、创建请求组装、checkId 确定性、docx/联调风格映射样本基线、JKDA00002 验证入口 - 标准启动路径:`./init.sh` 已配置三步:依赖下载、`go test ./...`、`go run . -mode server -config config.yaml.example` - 标准验证路径:`go test ./...`、`go build ./...` -- 当前 blocker:无硬 blocker。软限制:当前机器从 Git Bash 启动 Go 会出现标准库路径/构建缓存权限异常,`init.sh` 无法完整跑完;PowerShell 下等价 Go 命令和真实请求通过。厂家侧 B1/B2/B4/B5 契约缺口只影响阶段 4,不阻塞当前 T-206 路径 +- 当前 blocker:T-206 真实 create 验收缺少可写入沙箱的安全测试居民资料或明确写入授权。软限制:当前机器从 Git Bash 启动 Go 会出现标准库路径/构建缓存权限异常,`init.sh` 无法完整跑完;PowerShell 下等价 Go 命令和真实查询通过。厂家侧 B1/B2/B4/B5 契约缺口只影响阶段 4 ## 当前目录要点 @@ -22,23 +22,23 @@ | `main.go` | 已有 | 单入口,解析 `-mode server|deliver`、`-config`,并提供 `-verify-jkda` 联调验证入口(身份证从 `OSI_VERIFY_ID_CARD` 读取) | | `config/` | 已有 | viper 配置加载,支持 `OSI_*` 等环境变量覆盖 | | `contract/envelope.go` | 已有 | OSI 通用信封与 `manageInfo` 结构 | -| `contract/jkda.go` | 已有 | JKDA00002 查询响应结构体;创建请求结构体留到 T-206 | +| `contract/jkda.go` | 已有 | JKDA00002 查询响应结构体;JKDA00001/00003 创建更新请求结构体与保存结果结构体 | | `osi/sign.go` | 已有 | OSI 请求头 MD5 签名与 headers 组装 | | `osi/transport.go` | 已有 | OSI JSON POST 传输层;原始 HTTP/1.1 写请求以保留头名大小写;支持超时与可选 SOCKS5 | -| `osi/codes.go` | 已有 | serviceId 路由与成功/可重试判码;已包含 JKDA 与四个公开查询 serviceId | -| `osi/client.go` | 已有 | 通用 `Call`:签名头 + 信封 + 传输 + 响应解析 | -| `osi/jkda.go` | 已有 | JKDA00002 Find 与 JKDA00005 FindRqbj | +| `osi/codes.go` | 已有 | serviceId 路由与成功/可重试判码;已包含 JKDA 查询/创建/更新与四个公开查询 serviceId | +| `osi/client.go` | 已有 | 通用 `Call`:签名头 + 查询信封 + 传输 + 响应解析;内部 `callUploadInfo` 支持创建/更新完整 uploadinfo | +| `osi/jkda.go` | 已有 | JKDA00001 Create、JKDA00003 Update、JKDA00002 Find、JKDA00005 FindRqbj | | `osi/public.go` | 已有 | WGDZ00001 网格、ZRYS00001 责任医生、YPML00001 药品、CXJG00002 机构查询 | | `cache/dictionary.go` | 已有 | 公开字典内存快照;按名称反查 `regionCode`/`manaDoctorId`/`manaUnitId`;可选持久化失败不阻断 | | `verify_jkda.go` | 已有 | JKDA00002 真实请求验证辅助入口 | | `config.yaml.example` | 已有 | 占位配置,不含真实凭据 | | `go.mod` `go.sum` | 已有 | module `chis_osi`,依赖 viper 与 `golang.org/x/net/proxy` | -| `docs/` | 已有 | 设计文档集 01~06 + 本快照;`docs/04 §9` 记录公开查询脱敏实测契约;`账号.txt` 本地留存不入库 | +| `docs/` | 已有 | 设计文档集 01~06 + 本快照;`docs/04 §9` 记录公开查询脱敏实测契约,`docs/04 §10` 记录创建/更新本地契约;`账号.txt` 本地留存不入库 | | `tasks.md` `progress.md` | 已有 | 任务看板 / 执行流水(根目录) | | `scripts/` | 已有·不入库 | Python 联调脚本(硬编码真实凭据与身份证,勿提交) | | `config.yaml` | 已有·不入库 | 真实/本地配置;不要提交 | | `mapping/dict.go` | 已有 | 性别/民族/血型/RH/文化程度/职业/婚姻/医保/personSign 双向码表;未命中返回 `ValidationError` | -| `mapping/health_record.go` `mapping/checkid.go` | 已有 | 健康档案映射草稿、字典快照注入、主数据反查、必填/码表校验、确定性 checkId | +| `mapping/health_record.go` `mapping/checkid.go` | 已有 | 健康档案映射草稿、字典快照注入、主数据反查、创建请求组装(含 createUser/createUnit 派生)、必填/码表校验、确定性 checkId | | `mapping/health_record_baseline_test.go` | 已有 | docx 风格、联调风格、脏数据映射回归样本 | | `pipeline/` 等 | 待建 | 后续任务 | @@ -46,6 +46,7 @@ - 签名:`password = md5("ts=<13位毫秒ts>&ask=")` 32 位小写,与 Python `hashlib` 向量一致。 - 信封:查询也走 `{"serviceId", "uploadinfo": {"baseInfo", "manageInfo"}}`,见 `docs/04 §8` 与 `docs/04 §9`。 +- 创建/更新信封:`baseInfo`、`manageInfo`、`healthRecord` 等节点在 `uploadinfo` 下同级;本地契约见 `docs/04 §10`,真实 create 样本待补。 - 成功码:字符串 `"01"`(判定按去前导零 == `"1"`),见 `docs/01 §1`。 - `405` 为平台服务调用超时,可重试。 - 机构码分层:请求头 `orgCode`=18 位统信码 ≠ `manaUnitId`=9 位机构码 ≠ 12 位区划码,见 `docs/01 §3`。 @@ -54,6 +55,7 @@ - 真实平台/代理对请求头名大小写敏感;Go `net/http` 会规范化头名,OSI 传输层需保留 `orgCode/deviceSN/userName` 的原始大小写。裸写传输层默认请求 `Accept-Encoding: identity`,避免收到未解压压缩响应。 - 公开字典查询返回 `data` 数组;网格、责任医生、机构三类已真实验证可作为 T-103 字典缓存来源。 - `cache.DictionarySnapshot` 可作为 `mapping.MapContext.MasterData` 注入;PHIS 只有名称、缺平台码时,映射层可反查补齐主数据,未命中仍按必填错误处理。 +- 创建请求侧门牌号字段为 `addressNumber`;查询响应侧实测字段为错拼 `adressNumber`,两者分开建模。 ## 当前可运行内容 @@ -78,8 +80,8 @@ python3 scripts/query_health_record.py ## 下一步 -1. T-206:健康档案 Create/Update + 创建请求结构体;用真实字典快照补齐 `regionCode/manaDoctorId/manaUnitId` 后跑创建闭环。 -2. T-204:T-206 完成后建立 server 模式 `/api/health-record/save`。 +1. 解除 T-206 阻塞:提供可写入沙箱的安全测试居民资料,或明确授权使用某条测试数据执行 `JKDA00001` create。 +2. T-206 真实 create 成功后,补 `phrId` 返回样本与 `checkId` 幂等语义,再进入 T-204 server 模式 `/api/health-record/save`。 ## 维护规则 diff --git a/progress.md b/progress.md index 7af95c1..d62c1ae 100644 --- a/progress.md +++ b/progress.md @@ -172,3 +172,16 @@ - 验证:`go build ./...` 通过。 - 决策:T-103 不引入 Redis 客户端依赖,只定义可选持久化接口;持久化失败被忽略,内存快照仍刷新,满足“redis 不可用不阻断”。 - 下一步:T-206(健康档案 Create/Update + 创建请求契约)。 + +## 2026-07-07 T-206 健康档案 Create/Update(代码完成,真实写入受阻) + +- 状态:BLOCKED(代码与本地验证完成;真实 create 写入平台待安全测试档案/写入授权)。 +- 变更:`contract/jkda.go` 新增 `HealthRecordCreate`、`HealthRecordBaseInfo`、`HealthRecordCreateInfo`、`HealthRecordSaveResult`;创建请求侧使用 `addressNumber`,保留查询响应侧 `adressNumber`。 +- 变更:`osi/codes.go` 新增 `JKDA00001`/`JKDA00003` 与 `/jkda/create`、`/jkda/update` 路由;`osi/client.go` 增加内部 `callUploadInfo`,支持创建/更新把 `healthRecord/pastHistory/...` 放在 `uploadinfo` 同级节点;`osi/jkda.go` 新增 `CreateHealthRecord`、`UpdateHealthRecord`。 +- 变更:`mapping.BuildHealthRecordCreate` 将 `MappedHealthRecord` 转为创建请求契约,保留确定性 `checkId` 与 `regionCode/manaDoctorId/manaUnitId`,并派生 `createUser/createUnit`。 +- RED:`go test ./contract -run TestHealthRecordCreateSerializesCreateFieldNames -count=1` 初次失败,缺少创建请求结构体;`go test ./osi -run "Test(Create|Update)HealthRecord" -count=1` 初次失败,缺少 create/update 方法与 serviceId;`go test ./mapping -run TestBuildHealthRecordCreateUsesMappedFieldsAndCheckID -count=1` 初次失败,缺少构建函数。 +- GREEN:补最小实现后上述三组目标测试通过。 +- 验证:`go test ./contract ./osi ./mapping -count=1` 通过。 +- 验证:`go test ./...` 通过。 +- 验证:`go build ./...` 通过。 +- 阻塞:未执行真实 `JKDA00001` create。原因是当前只有真实查询样本,没有明确的安全测试居民/身份证和写入授权;直接用现有真实样本创建/更新可能污染平台档案或触发重复建档。解除条件:提供可写入沙箱的测试居民资料,或明确授权使用某条测试数据做 create。 diff --git a/tasks.md b/tasks.md index 0c395df..811bc66 100644 --- a/tasks.md +++ b/tasks.md @@ -59,7 +59,7 @@ | --- | --- | --- | --- | --- | | T-101 | `osi/public.go` 四个字典查询(网格/责任医生/药品/机构) | T-005 | 真实机构码能查到下级网格、责任医生、机构树 | DONE | | T-103 | 字典缓存(内存 + redis 可选) | T-101, T-102 | 映射层能反查 `regionCode/manaDoctorId/manaUnitId`;redis 不可用不阻断 | DONE | -| T-206 | `osi/jkda.go`:**Create/Update** + `contract/jkda.go` 补创建请求结构体 | T-201, T-202, T-103 | 一条档案经映射(真实字典快照)→ create → 平台返回成功码与 `phrId`;Update 沿用 checkId | TODO | +| T-206 | `osi/jkda.go`:**Create/Update** + `contract/jkda.go` 补创建请求结构体 | T-201, T-202, T-103 | 一条档案经映射(真实字典快照)→ create → 平台返回成功码与 `phrId`;Update 沿用 checkId | BLOCKED(代码已完成;待安全测试档案/写入授权做真实 create) | | T-204 | `handler`+`router`:`/api/health-record/save` | T-206 | server 模式起服务,curl 全链路返回投递结果 | TODO | ## 里程碑