Files
chis_osi/progress.md
T
ilaandClaude Opus 4.8 f6a7f61d59 docs: 同步 T-206 状态(BLOCKED,代码完成待真实 create)
- tasks.md T-206 标 BLOCKED 并注明阻塞原因
- docs/04 主数据反查落到 MapContext.MasterData
- progress/current-state 追加 T-206 记录

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:11:23 +08:00

188 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 执行进度记录
> 只追加的历史流水:每轮做了什么、跑了什么验证、遇到什么阻塞、做了什么决策。
> 当前目录/命令/下一步等可覆盖快照写 [`docs/current-state.md`](docs/current-state.md),不在本文重复。
> 任务状态以 [`tasks.md`](tasks.md) 为准。
## 记录格式
```markdown
## YYYY-MM-DD T-编号 任务名(或事件名)
- 状态:DONE / BLOCKED / PARTIAL
- 变更:改了哪些文件或模块
- 验证:运行的真实命令和结果
- 阻塞:如有,写明原因和需要谁决策
- 决策:如有,记录本轮确定的关键取舍
- 下一步:建议下一个任务 ID 或待确认事项
```
## 执行记录
## 2026-05~06 设计文档集初始化(pre-code)
- 状态:DONE
- 变更:`docs/01~06` 设计文档集、`CLAUDE.md`、`.gitignore`(源材料不入库)。详见 `git log 3498542..1fd2569`。
- 决策:扁平目录布局对齐 chis_upload;OSI 为无状态 MD5 签名接口,不引入旧项目逆向复杂度;幂等键用 checkId。
## 2026-07-06 JKDA00002 联调打通 + 文档校准
- 状态:DONE
- 变更:`scripts/query_health_record.py`(本地,不入库)打通真实查询;据实测样本校准 `docs/01`(成功码/信封/机构码分层/坑点坐实)、`docs/04`(新增 §8 实测契约)、`docs/06`(A1~A9/B3/B6/C3/D2/D3 回填)。
- 验证:沙箱真实请求返回 `code="01" message="操作成功"`,`data` 为档案聚合数组;脚本离线自检(编译 + 信封 + 判码逻辑)通过。
- 决策:**成功码为 `"01"` 非 docx 所示 `"1"`**,判定按去前导零;**查询也走 `uploadinfo` 信封**;请求头 `orgCode` 用 18 位统一社会信用代码(9 位码是 `manaUnitId`,两者不可混用)。
- 阻塞:`tests/test_query_health_record.py` 测的是旧脚本 API,已失效(import 报错)→ 登记为 T-000。
- 下一步:T-000,然后 T-001 起步阶段 0。
## 2026-07-06 接入 harness coding 文档工件
- 状态:DONE
- 变更:新建 `tasks.md`(看板)、`progress.md`(本文)、`docs/current-state.md`(快照)、`init.sh`(统一验证入口)、`AGENTS.md`(通用 agent 薄入口);更新 `CLAUDE.md` 会话启动流程与文档同步表、`docs/README.md` 导航、`docs/05` 成功码勘误。
- 决策:任务看板放根目录 `tasks.md`;现有 `docs/01~06` 设计文档不重命名不重排;模板可选增强集(rubric/quality/method-map)暂不引入。
- 下一步:T-000。
## 2026-07-06 T-000 处理失效测试
- 状态:DONE
- 变更:删除 `tests/`(`test_query_health_record.py` 及 `__pycache__`)——文件从未被 git 跟踪,无需 `git rm`。
- 验证:`git status --short` 确认删除前后均无该目录的跟踪历史;删除后工作区干净。
- 决策:不改写为新契约的最小测试,直接删除——脚本是硬编码的本地联调工具(不入库),暂不需要自动化测试覆盖。
- 下一步:T-001,进入路线图阶段 0(go module + 双子命令骨架)。
## 2026-07-06 T-001 初始化 Go module 与双子命令骨架
- 状态:DONE
- 变更:新增 `go.mod`/`go.sum`、`main.go`、`main_test.go`、`config/config.go`、`config/config_test.go`、`config.yaml.example`;`tasks.md` 标记 T-001 完成;`docs/05` 勾选阶段 0 第一项。
- 验证:`go test ./...` 通过;`go build ./...` 通过;`go run . -mode server -config config.yaml.example` 输出 `chis_osi mode=server`;`go run . -mode deliver -config config.yaml.example` 输出 `chis_osi mode=deliver`。
- 验证补充:`bash init.sh` 在当前 Windows 环境失败,系统提示未安装 WSL;已用等价 Go 命令完成验收。
- 决策:Go module 使用本地模块名 `chis_osi`;`main.go` 只打印 mode,不打印配置值,避免泄露真实环境信息;`config.yaml.example` 只保留占位值。
- 下一步:T-002(`osi/sign.go` MD5 头签名 + 单测)。
## 2026-07-06 T-002 MD5 头签名
- 状态:DONE
- 变更:新增 `osi/sign.go` 与 `osi/sign_test.go`;`BuildHeaders` 组装 OSI 必需请求头,`SignPassword` 按 `md5("ts=<ts>&ask=<ask>")` 输出 32 位小写;`tasks.md` 标记 T-002 完成;`docs/05` 勾选阶段 0 第二项。
- 验证:先运行 `go test ./osi` 看到缺少 `SignPassword`/`BuildHeaders`/`HeaderInput` 的预期失败;实现后 `go test ./osi` 通过;`go test ./...` 通过;`go build ./...` 通过。
- 决策:固定测试向量 `ts=1700000000123&ask=secret-key -> 008aceff8247cb42d2a99b2c48d0ac88` 来自 Python `hashlib.md5(...).hexdigest()`,用于对齐本地联调脚本签名算法;`ask` 只参与签名,不进入 headers。
- 下一步:T-003(`osi/transport.go` 传输层,保留 SOCKS5/超时)。
## 2026-07-06 看板重排(查询先行)
- 状态:DONE(规划调整,无代码)
- 变更:`tasks.md` 把 Phase 1(字典)/Phase 2(档案) 重排为 Phase Q(查询)→M(映射)→D(字典+创建);`osi/jkda.go` 拆为 T-203(Find/FindRqbj) 与 T-206(Create/Update);`docs/05` 加执行顺序注记。任务 ID 不变。
- 决策:查询档案(Find)不依赖字典,映射纯函数单测只需码表+注入假字典快照,只有真实创建闭环才需 T-101/T-103 字典反查——故把字典接口降到创建之前、查询之后。顺带把"Create 依赖字典缓存"从隐性依赖显式化到 T-206。
- 下一步:仍是 T-003(Phase 0 未变),Phase 0 完成后按 Q→M→D 领取。
## 2026-07-06 T-003 OSI 传输层骨架
- 状态:DONE
- 变更:新增 `osi/transport.go` 与 `osi/transport_test.go`;传输层支持 JSON POST、请求超时、可选 SOCKS5 代理;不设置 Cookie/Origin/Referer/X-Requested-With 等网页拟态头;`tasks.md` 标记 T-003 完成;`docs/05` 勾选阶段 0 传输层项。
- 验证:先运行 `go test ./osi` 看到 `NewTransport`/`TransportConfig` 未定义的预期失败;实现后 `go test ./osi` 通过;`go test ./...` 通过;`go build ./...` 通过。
- 决策:`socks5_proxy` 支持 `host:port` 和 `socks5://host:port` 两种写法;非空时只配置 OSI 客户端代理,不做直连回退;传输层只返回 HTTP status 和原始响应体,业务判码留给 T-004 的 client/codes。
- 下一步:T-004(`osi/client.go` `Call` + `osi/codes.go` + `contract/envelope.go`)。
## 2026-07-06 T-004 OSI Client Call + 信封 + 判码
- 状态:DONE
- 变更:新增 `contract/envelope.go`、`osi/codes.go`、`osi/client.go` 及对应单测;`Client.Call` 负责拼 `/osi/api` 路径、组装 MD5 签名请求头、构造 `serviceId + uploadinfo{baseInfo,manageInfo}` 信封、发送 JSON、解析 `{code,message,data}`;`tasks.md` 标记 T-004 完成;`docs/05` 勾选 client/codes/envelope 两项。
- 验证:先运行 `go test ./contract ./osi` 看到 `Envelope`、`NewClient`、`IsSuccessCode` 等未定义的预期失败;实现后 `go test -count=1 ./contract ./osi` 通过;`go test ./...` 通过;`go build ./...` 通过。
- 决策:成功码统一按去前导零后等于 `"1"` 判定,兼容实测 `"01"`;`405` 归类为可重试;`osi.base_url` 按主机地址处理,Client 自动拼 `/osi/api`,同时兼容已带 `/osi/api` 的输入。
- 下一步:T-005(阶段 0 验收:Go 侧真实请求打通 + 配置 `init.sh`)。
## 2026-07-06 T-005 阶段 0 验收:Go 侧真实请求打通 + init.sh
- 状态:DONE
- 变更:新增 `verify_jkda.go`/`verify_jkda_test.go`,`main.go` 增加 JKDA00002 验证入口(T-006 后改为 `-verify-jkda` + `OSI_VERIFY_ID_CARD`);`config` 支持 `OSI_*` 等环境变量覆盖;`osi/transport.go` 改为按原始 HTTP/1.1 写请求以保留 `orgCode/deviceSN/userName` 头名大小写;`init.sh` 配置依赖下载、`go test ./...`、启动三步命令;补充传输层头名与基础请求头单测。
- 验证:`go test ./...` 通过;`go build ./...` 通过;使用本地环境变量发起 Go 版 JKDA00002 真实请求,返回 `JKDA00002 code=01 message=操作成功 data_count=1`。
- 验证补充:`init.sh` 三命令已替换;当前机器从 Git Bash 启动 Go 会出现标准库路径/构建缓存权限异常,导致脚本运行环境未能完整验收。等价 PowerShell 下直接执行 `go test ./...`、`go build ./...` 与 Go 真实请求均通过。
- 决策:真实平台/代理对请求头大小写敏感,Go `net/http` 会规范化头名并触发 EOF;传输层因此对 OSI 调用使用原始 HTTP/1.1 写入,保留 Python requests 已验证的头名大小写与基础头语义。
- 下一步:T-201(`contract/jkda.go` 查询响应结构体)。
## 2026-07-06 T-006 Phase 0 review hardening
- 状态:DONE
- 变更:`osi/transport.go` 默认 `Accept-Encoding` 改为 `identity`,避免声明 gzip/br 却不解压;删除传输层未使用的 `http.Client`/roundTripper 残留;`main.go` 验证入口改为 `-verify-jkda` 并从 `OSI_VERIFY_ID_CARD` 读取身份证;删除 `contract.Envelope` 顶层 `BaseInfo`,保留 `uploadinfo.baseInfo`;同步 `docs/03`、`docs/current-state.md` 与 `tasks.md`。
- 验证:按 TDD 先看到 `TestTransportRequestsIdentityEncoding`、`TestEnvelopeDoesNotExposeTopLevelBaseInfo`、`TestResolveVerifyIDCard*` 失败;实现后目标测试通过;`go test ./...` 通过;`go build ./...` 通过;`git diff --check` 通过。
- 决策:Phase 0 先避免压缩响应风险,不实现 gzip/br 解压;https 支持仍按当前沙箱事实 fail-fast,生产若切 https 需另起任务处理 TLS 下的头名保留策略。
- 下一步:T-201(`contract/jkda.go` 查询响应结构体)。
## 2026-07-06 复评 T-006 + 登记延期任务 T-007
- 状态:DONE(评审与看板维护)
- 变更:复评 T-006 对 `docs/review/2026-07-06-phase0-review.md` 的修复情况;`tasks.md` 新增延期任务 **T-007(传输层 https/TLS 支持,生产前 gate)**,落地 review P1-2 的"延期须登记"要求。
- 复评结论:T-006 认领的 4 项(gzip 声明/传输死代码/命令行 PII/Envelope 冗余)均已修复并补测;review 其余项中——P1-2 https 以 T-007 登记延期;P2-2(裸写根因抓包对照)、P3(nil-transport 静默兜底、UA 写死、init.sh 本机不通)仍未处理,作为后续小整改候选。P1-1(未提交即标 DONE)本轮 T-005/T-006 均已补交、不再漂移。
- 决策:https 是否阻断取决于生产地址协议(挂钩 docs/06 A10);沙箱 http 不阻塞当前开发,故 T-007 置于 Backlog 而非当前 Phase。
- 下一步:T-201。
## 2026-07-07 T-201 JKDA00002 查询响应契约
- 状态:DONE
- 变更:新增 `contract/jkda.go` 与 `contract/jkda_test.go`;按 `docs/04 §8` 建立 `FindHealthRecordResponse`、`FindHealthRecord`、`HealthRecord`、`PastHistory`、既往疾病/手术/外伤/输血数组和 `FamilyMiddle` 查询响应结构体;保留响应侧错拼字段 `adressNumber`;`checkId`、`insuranceType`、`otherPersonGroup` 等 nullable 字段使用指针。
- 验证:按 TDD 先看到 `go test ./contract -run TestFindHealthRecordDeserializesMeasuredJKDA00002Data -count=1` 因 `FindHealthRecord` 未定义失败;补结构体后通过。随后看到 `go test ./contract -run TestFindHealthRecordResponseDeserializesDataArray -count=1` 因 `FindHealthRecordResponse` 未定义失败;补响应结构体后通过。最终 `go test ./contract -count=1` 通过,`go test ./...` 通过。
- 决策:T-201 只建查询响应契约;创建/更新请求结构体仍按看板留到 T-206,避免提前把未联调字段固化。
- 下一步:T-203(`osi/jkda.go`:Find + FindRqbj)。
## 2026-07-07 T-203 JKDA Find / FindRqbj
- 状态:DONE
- 变更:新增 `osi/jkda.go` 与 `osi/jkda_test.go`;实现 `Client.FindHealthRecord`(JKDA00002 `/auto/jkda/find`)和 `Client.FindRqbj`(JKDA00005 `/jkda/findrqbj`);查询入参按单一 key 校验,支持 `idCard`、`empiId`、`phrid`、`personName`(Find)以及 `idCard`/`phrid`(FindRqbj);`verify_jkda.go` 改为复用正式 Find 方法。
- 验证:按 TDD 先运行 `go test ./osi -run "TestFind(HealthRecord|Rqbj)" -count=1`,因 `FindHealthRecord`/`FindRqbj` 与查询类型未定义失败;实现后目标测试通过。随后 `go test . -run TestRunJKDAFindCheckUsesConfiguredOSIClient -count=1` 通过,`go test ./...` 通过;使用本地环境变量发起正式 Go Find 真实请求,返回 `JKDA00002 code=01 message=操作成功 data_count=1`。
- 决策:FindRqbj 已有 serviceId/path 与 `personSign` 契约,先以单测覆盖;当前只对 JKDA00002 Find 做真实请求验收,因为本地已有可查档案样本。
- 下一步:T-102(`mapping/dict.go` 全量码表)。
## 2026-07-07 T-102 映射码表基线
- 状态:DONE
- 变更:新增 `mapping/dict.go` 与 `mapping/dict_test.go`;建立通用 `Dict`/`Entry` 双向查表结构和 `ValidationError`;落地性别、民族(01~56 + 99 其他)、血型、RH、文化程度、职业、婚姻、医保、人群标记 `personSign` 初始码表。
- 验证:按 TDD 先运行 `go test ./mapping -count=1`,因 `SexDict`/`Dict`/`ValidationError` 等未定义失败;实现后 `go test ./mapping -count=1` 通过;补充民族 `01~56` 连续码检查后再次通过。最终 `go test ./...` 通过,`go build ./...` 通过。
- 决策:当前文档未给 PHIS 侧差异码值,T-102 先以 PHIS 与 OSI 同码作为静态基线;保留 `Entry{PHIS, OSI}` 结构,后续 T-202 若发现 PHIS 码值差异可局部覆盖,不改调用方。
- 下一步:T-202(`mapping/health_record.go` + `mapping/checkid.go`)。
## 2026-07-07 T-202 健康档案映射与 checkId
- 状态:DONE
- 变更:新增 `mapping/health_record.go`、`mapping/checkid.go` 与 `mapping/health_record_test.go`;建立 PHIS 健康档案输入、映射层 `MappedHealthRecord` 草稿、`MapContext`/`DictSnapshot` 注入点;主数据直传字段、码表字段、必填校验、结构化 `ValidationError` 与确定性 `GenerateCheckID` 已覆盖。
- 验证:按 TDD 先运行 `go test ./mapping -run "TestMapHealthRecord|TestGenerateCheckID" -count=1`,因 `MapHealthRecord`/`MapContext`/`GenerateCheckID` 未定义失败;实现后目标测试通过。最终 `go test ./mapping -count=1`、`go test ./...`、`go build ./...`、`git diff --check` 均通过。
- 决策:创建请求结构体仍留到 T-206;T-202 只输出映射层草稿结构,先把纯函数、校验、字典注入和 checkId 稳定性打牢。
- 下一步:T-205(映射单测基线:docx 样例 + 联调样本)。
## 2026-07-07 T-205 映射单测基线
- 状态:DONE
- 变更:新增 `mapping/health_record_baseline_test.go`;补 docx 风格完整样例、联调样本风格脱敏样例、脏数据样例三组映射回归测试,覆盖主数据字段映射、码表转换、必填校验、码表未命中和 checkId 稳定生成。
- 验证:`go test ./mapping -count=1` 通过;`go test ./...` 通过。
- 决策:样本全部脱敏,不提交本地真实身份证/姓名/电话;T-205 只建立映射测试基线,不扩展创建请求结构体。
- 下一步:T-101(`osi/public.go` 四个字典查询)。
## 2026-07-07 T-101 公开字典查询
- 变更:新增 `osi/public.go`,实现 `QueryGridAddress`、`QueryDoctors`、`QueryDrugs`、`QueryOrgs` 四个公开查询薄封装;补充 `WGDZ00001`、`ZRYS00001`、`YPML00001`、`CXJG00002` serviceId 路由。
- 测试:先补 `osi/public_test.go`,覆盖四个接口的 path、serviceId、baseInfo 组装与 data 解码。
- RED:`go test ./osi -run "TestQuery(GridAddress|Doctors|Drugs|Orgs)" -count=1` 初次失败,缺少公开查询类型、serviceId 常量与方法。
- GREEN:`go test ./osi -run "TestQuery(GridAddress|Doctors|Drugs|Orgs)" -count=1` 通过。
- 验证:`go test ./osi -count=1` 通过。
- 验证:`go test ./...` 通过。
- 验证:`go build ./...` 通过。
- 联调:使用本地未入库脚本中的沙箱凭据和身份证,仅在临时 Go 程序内读取;先 JKDA00002 取一条档案主数据,再调用公开查询。结果:Find `code=01 count=1`;责任医生 `code=01 count>0`;机构 `code=01 count>0`;网格 `code=01 count>0`。未输出真实身份证、机构码、医生姓名或密钥。
- 说明:药品目录查询本次完成客户端封装和单测,T-101 验收要求未要求真实药品目录联调;后续需要用药品关键字/拼音码时再补真实样本。
## 2026-07-07 T-103 字典缓存
- 状态:DONE
- 变更:新增 `cache/dictionary.go` 与 `cache/dictionary_test.go`;建立 `DictionarySnapshot` 内存快照,支持按名称反查 `regionCode`、`manaDoctorId`、`manaUnitId`;新增 `DictionaryService.Refresh`,从 T-101 公开查询结果刷新内存快照,并通过可选 `PersistentStore` 保存。
- 变更:`mapping.PHISHealthRecord` 增加 `RegionName`、`ManaDoctorName`、`ManaUnitName`;`MapContext` 增加 `MasterDataLookup`,当 PHIS 未直接提供平台码时,映射层可通过主数据快照反查补齐必填主数据。
- RED:`go test ./cache -count=1` 初次失败,缺少 `NewDictionarySnapshot`、`NewDictionaryService`、`RefreshParams`、`DictionarySnapshot`;`go test ./mapping -run TestMapHealthRecordUsesMasterDataLookupWhenCodesMissing -count=1` 初次失败,缺少名称字段与 `MasterData` 上下文。
- GREEN:补最小实现后,`go test ./cache -count=1` 通过;`go test ./mapping -run TestMapHealthRecordUsesMasterDataLookupWhenCodesMissing -count=1` 通过。
- 验证:`go test ./cache ./mapping -count=1` 通过。
- 验证:`go test ./...` 通过。
- 验证:`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。