175 lines
18 KiB
Markdown
175 lines
18 KiB
Markdown
# 执行进度记录
|
||
|
||
> 只追加的历史流水:每轮做了什么、跑了什么验证、遇到什么阻塞、做了什么决策。
|
||
> 当前目录/命令/下一步等可覆盖快照写 [`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 + 创建请求契约)。
|