# 执行进度记录 > 只追加的历史流水:每轮做了什么、跑了什么验证、遇到什么阻塞、做了什么决策。 > 当前目录/命令/下一步等可覆盖快照写 [`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=&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。 ## 2026-07-08 T-208 server 模式健康档案查询端点 - 状态:DONE - 变更:新增 `handler/health_record.go`(`GET /api/health-record/find`,回写平台完整响应,不裁字段)、`handler/health_record_test.go`(完整回写 + 缺标识符 400)、`server.go`(server 模式起 HTTP 服务 + 路由)、`osi_client.go`(抽出 `buildOSIClient` 共用构造);`verify_jkda.go` 改用共用构造消除重复;`main.go` 加 `-addr`、server 模式真正启动服务;`tasks.md`/`current-state.md` 同步。 - 验证:用户在 Windows 侧 `go test ./...`、`go build ./...` 通过;`go run . -mode server` 起服务后 `curl .../find?idCard=<..>` 返回平台完整档案 JSON。(本 WSL 离线无 go 工具链,未在本环境复跑。) - 决策:查询端点直接回写 `Result.Raw`(平台原始 `{code,message,data}`),保证未建模字段不丢失;默认监听 `127.0.0.1:8080` 而非 `:8080`,因端点返回真实档案 PII,避免绑 0.0.0.0 暴露到局域网;身份证经 URL query 传入,仅作本机查看工具,勿反代外网。 - 下一步:解 T-206 阻塞(真实 create),或按需为查询端点加鉴权后再考虑对外。 ## 2026-07-08 看板重排(读先行:查询优先、写入后置) - 状态:DONE(规划调整,无代码) - 变更:`tasks.md` 新增 Phase Q2(其余业务线查询:T-301 体检查询 / T-302 体检端点 / T-303 老年人查询 / T-304 列表查询),把写入 T-206(BLOCKED)/T-204 明确降到 Q2 之后;里程碑加 M4=体检查询、M5=创建闭环。`docs/06` 顶部加"★ 当前批次优先催办",把写入授权(D3)+缺失 serviceId(B1/B2/B4)+写入规则(C1/C2) 归拢成一封邮件一起催。`docs/current-state.md` 下一步改为 T-301 起。 - 决策(全栈分析):真实 create 被厂家写入授权外部锁死,垂直切片走不通;改按读/写横切、读先行——读路径不被授权阻塞、只读零风险、且各查询响应是将来写入映射的事实侦察。T-206 创建代码保留不作废,授权到位再验收。 - 下一步:T-301 体检查询打通。 ## 2026-07-08 T-301 体检查询打通(最近一次) - 状态:DONE - 变更:新增 `contract/jktj.go`(HealthCheckSummary 定位字段)、`osi/jktj.go`(`LastHealthCheck` JKTJLSJL00002)、`osi/jktj_test.go`(脱敏假数据测路径/解码/Raw/idcard 小写映射/单键校验);`osi/codes.go` 登记 JKTJLSJL00002 + JKTJLIST00002 路由;`docs/04` 新增 §11 体检字段清单(~260 项/7 节点),`docs/01 §5.2` 标注各体检接口联调状态。 - 验证:真实"最近一次体检"查询已通(用户侧 Python 脚本 code=01,返回单个体检对象)。Go 侧因本环境离线无工具链未复跑,需用户 `go test ./...` 确认编译与单测。 - 发现:① 单条 JKTJ00002 `auto/jktj/query` 平台实测"没有url的接口配置"(未部署),`auto/jktj/find` 待确认 → docs/06 B5。② 体检 data 为单对象(档案 find 是数组)。③ 体检身份证字段小写 `idcard`(档案 `idCard`)。④ 体检主键 `checkId`(与档案幂等 checkId 同名不同义),子节点 `healthCheck` 外键指向它。 - 决策:查询只强类型化定位字段,完整内容留 `Result.Raw`;~260 全量字段建模留到体检 create(写入才需逐字段,避免照单样本猜 null 类型)。 - 下一步:T-302 体检 HTTP 查询端点(复用 T-208 handler 模式);或按 docs/06 催厂家确认单查/列表接口。 ## 2026-07-08 拆分 T-305 体检名单查询、收窄 T-304 - 状态:DONE(看板+文档维护) - 变更:从 docx 扒出 JKTJLIST00002 完整契约(checkYear/page/rows 在 baseInfo,返回人员名单+checkType 状态),记入 `docs/04 §11.4`;`tasks.md` 拆出 **T-305(体检已检/未检名单,契约齐全仅待沙箱部署验证,TODO 可领)**,把 T-304 收窄为真正缺 serviceId 的列表(档案/老年人/中医指导);`scripts/query_health_check.py` 的 list 模式改为 docx 正确参数。 - 决策:JKTJLIST00002 契约不缺(docx 完整),不该被 B1/B2 阻塞——只差运行时确认端点是否部署,故独立成可领任务;纠正原 T-304"一刀切 BLOCKED"把能做的和真卡的混在一起。它是名单/进度接口,不含体检明细,非数据源。 - 下一步:跑 list 探针确认部署 → 若通即领 T-305 建 osi 方法;未部署则归 docs/06 B5 催厂家。 ## 2026-07-08 T-305 体检已检/未检名单查询打通 - 状态:DONE - 变更:`contract/jktj.go` 加 `HealthCheckPerson`(名单一行 20 字段,字段名按实测校正);`osi/jktj.go` 加 `ListHealthCheckPeople`(JKTJLIST00002)+ `ListHealthCheckQuery`(checkYear 必填、page/rows 默认、idCard/checkType 可选);`osi/jktj_test.go` 加名单解码/路径/baseInfo分页/checkYear 校验测试(脱敏假数据);`docs/04 §11.4` 校正实测字段名并标端点已部署;`docs/01 §5.2` 标 JKTJLIST00002 联调可用。 - 验证:Python 探针 `list` 实测 `code=01`,返回 10 人名单(每人 checkType 状态)。Go 侧待用户 `go test ./...` 复验(本环境离线)。 - 发现:实测字段名与 docx 多处不符——`birthday`(非 birthDay)、`phrId`(非 phrid,驼峰)、`manaDoctorId`(非 manadoctorId),另有 docx 未列的 `manaDocterName`(拼写 Docter)/`empiId`/`manaUnitText`。再次印证以实测样本为准。 - 决策:名单行字段稳定且均为字符串(20 项),全部强类型化(区别于体检明细 260 项只留 Raw);单查 JKTJ00002 仍未部署,与本条无关。 - 下一步:T-302 体检 HTTP 端点;或按需把名单也暴露成 HTTP 端点。 ## 2026-07-08 T-302 体检 HTTP 查询端点 - 状态:DONE - 变更:新增 `handler/health_check.go`(`GET /api/health-check/last` 最近一次 + `/list` 年度名单,回写平台完整响应)、`handler/health_check_test.go`(脱敏假 OSI 后端测 last/list 的路径/原样回写/缺参 400);`server.go` 注册两条体检路由并打印;抽 `writeRawOrError` 共用回写。`tasks.md`/`docs/current-state.md` 同步。 - 验证:待用户 `go test ./...`、`go build ./...` 复验(本环境离线无工具链)。 - 决策:端点直接回写 `Result.Raw`(同档案端点 T-208),完整内容不裁字段;last 按 idCard/phrid/empiId,list 按 checkYear(必填)+idCard+checkType;默认仅绑 127.0.0.1(返回真实档案/名单 PII)。 - 下一步:T-303 老年人查询探针。 ## 2026-07-08 JKTJ00002 某人全部体检查询打通 - 状态:DONE - 变更:`osi/codes.go` 登记 `ServiceIDJKTJQuery=JKTJ00002` → `/auto/jktj/query`;`contract/jktj.go` 加 `HealthCheckRecordSummary`(驼峰 idCard、checkId 可空);`osi/jktj.go` 加 `QueryHealthChecks`(返回数组);`handler/health_check.go` 加 `/api/health-check/all` 端点;`server.go` 注册路由;`osi/jktj_test.go`/`handler/health_check_test.go` 加测试(含 null checkId 场景);`docs/01 §5.2`、`docs/04 §11.1` 校准。 - 验证:Python 探针实测 JKTJ00002 `code=01`,返回 8 条体检历史(2013–2026)。Go 侧待用户 `go test ./...`。 - 发现(实测校准):① JKTJ00002 身份证字段是**驼峰 `idCard`**(≠ 最近一次小写 `idcard`;体检三接口 last 独用小写);② **`checkId` 仅近年记录有值,历史记录为 `null`**(本样本仅最近 2 条有编号)——历史体检只能按 `checkDate` 定位;③ 数组元素结构同 §11.3。 - 决策:数组查询只强类型化定位字段(checkId/checkDate/idCard/personName),完整体检走 Result.Raw;单独建 `HealthCheckRecordSummary`(驼峰 idCard)不复用最近一次的 `HealthCheckSummary`(小写 idcard),因平台字段大小写不一致。 - 下一步:go test 复验后提交;体检查询三接口(最近一次/全部/名单)osi+HTTP 全齐。 ## 2026-07-08 新增 docs/07 本项目 HTTP 接口文档 - 状态:DONE - 变更:新建 `docs/07-本项目HTTP接口.md`,整理 server 模式 5 个查询端点(档案 find + 体检 last/all/list)的参数/上游 serviceId/返回形态/PII 警示/通用约定;`docs/README.md` 导航登记;`CLAUDE.md` 文档同步表加"HTTP 端点增改 → docs/07"。 - 决策:区分两层接口文档——上游 OSI 契约以 docs/01+04(实测)为准,本项目对外接口以 docs/07 为单一事实来源;docx 反复被证明不可靠,只作参考。 - 下一步:新增端点时同步 docs/07;对外前补鉴权/日志/限流。 ## 2026-07-09 登记 T-209 人群分类查询优化 - 状态:DONE(看板+契约文档维护) - 来源:用户更新 `scripts/query_crow_with_idcard.py`,并提供本地响应样本 `scripts/query_crow_with_idcard.json`(含真实身份证/phrId,不入库)。 - 发现:JKDA00005 实测请求路径为 `/osi/api/auto/jkda/findrqbj`,响应 `data` 是对象,字段含 `personSign`、`idCard`、`phrId`;当前 Go 侧只建模 `personSign`,且路径仍是早期 `/jkda/findrqbj`。 - 变更:`tasks.md` 新增 T-209;`docs/01`、`docs/04 §12` 登记实测契约和待改点。 - 下一步:实现 T-209,修正 `osi` 路由/响应结构,并补 server 模式人群分类查询端点。 ## 2026-07-09 T-209 人群分类查询优化 - 状态:DONE - 变更:`osi/codes.go` 修正 JKDA00005 路由为 `/auto/jkda/findrqbj`;`osi.PersonSignResult` 补 `idCard`、`phrId` 字段;`handler.HealthRecordHandler` 新增 `GET /api/health-record/crowd`,原样回写平台响应;`server.go` 注册新端点。 - RED:`go test ./osi -run TestFindRqbjCallsJKDA00005AndDecodesPersonSign -count=1` 初次失败,缺少 `idCard/phrId` 字段;`go test ./handler -run TestHealthRecordCrowdReturnsRawPlatformResponse -count=1` 初次失败,缺少 `Crowd` handler。 - GREEN:补实现后上述目标测试通过。 - 验证:`go test ./...` 通过。 - 验证:`go build ./...` 通过。 - 验证:`git diff --check` 通过。 - 决策:HTTP 端点继续采用查询类统一策略,直接回写 `Result.Raw`,避免裁掉平台后续新增字段;真实样本 JSON 仍只本地留存,不提交。 ## 2026-07-09 登记 T-210 公开查询 HTTP API - 状态:DONE(看板+文档维护) - 变更:`tasks.md` 新增 T-210,范围为把人群分类、网格地址、责任医生、药品目录、机构查询统一暴露为 server 模式 HTTP API;同步登记 `docs/openapi.yaml` 作为本项目 HTTP API 的机器可读文档。 - 决策:T-210 依赖 T-101(四类公开查询 OSI 客户端已完成)与 T-209(人群分类 HTTP 端点已存在)。实现时不再重复写 OSI 客户端,只补 handler/router、HTTP 测试、`docs/07` 与 OpenAPI。项目后续以内网部署为目标,但接口仍返回 PII/主数据,需在文档中明确绑定地址和鉴权边界。 - 验证:文档登记阶段运行 `git diff --check`;代码实现阶段再跑 `go test ./...`、`go build ./...`。 - 下一步:开始 T-210,实现公开查询 HTTP API。 ## 2026-07-09 T-210 公开查询 HTTP API - 状态:DONE - 变更:新增 `handler/public.go` 与 `handler/public_test.go`,暴露 `GET /api/dictionaries/grid-addresses`、`/doctors`、`/drugs`、`/orgs`;`server.go` 注册四个公开查询路由并打印启动提示;既有人群分类端点 `/api/health-record/crowd` 保持不变。 - 变更:同步 `docs/07-本项目HTTP接口.md` 与 `docs/openapi.yaml`,补网格地址、责任医生、药品目录、机构查询;更新内网部署说明,明确服务本身不内置鉴权。 - RED:`go test ./handler -run TestPublic -count=1` 初次失败,缺少 `NewPublicHandler`。 - GREEN:补最小实现后 `go test ./handler -run TestPublic -count=1` 通过。 - 验证:`go test ./...` 通过;`go build ./...` 通过;`python -c "import yaml; yaml.safe_load(open('docs/openapi.yaml', encoding='utf-8'))"` 通过;`git diff --check` 通过。 - 决策:公开查询端点继续原样回写平台 JSON,不裁字段;`pageNo` 做 HTTP 层整数校验,其余查询条件按上游查询透传。项目按内网部署推进,但 API 返回主数据/PII 相关上下文,生产暴露前仍需网关鉴权和日志脱敏。 - 下一步:T-303 老年人查询探针;或继续补内网部署鉴权/访问控制任务。 ## 2026-07-09 T-211 药品目录分页契约修正(登记) - 状态:DOING - 背景:docx YPML00001 药品目录查询是**分页列表**——`pageNo` 必填、有 `pageSize`、返回药品数组(ypxh/ypmc/ypdw/ypgg/ypjl/ycjl/jldw)。当前实现把 `pageNo` 当可选(`DrugQuery.PageNo` 带 omitempty、handler `parseOptionalInt`、docs/07 标"否"),且缺 `pageSize`——`pageNo=0` 会被 omitempty 丢弃,平台可能报必填错。 - 计划:`osi.DrugQuery` 去 pageNo omitempty + 加 PageSize;handler 缺 pageNo 返回 400 + 读 pageSize;`docs/07 §8`+`openapi.yaml` 标 pageNo 必填并补 pageSize;docx 契约未联调,全程注明待厂家样本核对。 - 注意:YPML00001 从未真实联调(current-state 自述),本次按 docx 校准,pageNo 是否真必填、响应字段名待联调确认;不扩 `Drug` 响应结构体(避免照单样本猜类型),HTTP 走 Raw 不受影响。 ## 2026-07-09 T-211 药品目录分页契约修正(完成) - 状态:DONE - 变更:`osi/public.go` `DrugQuery.PageNo` 去 omitempty(确保 pageNo=0 也上送)+ 加 `PageSize`;`handler/public.go` Drugs 改 pageNo 必填(新增 `parseRequiredInt`,缺则 400)+ 读 pageSize;`handler/public_test.go` 加"缺 pageNo→400"测试;`docs/07 §8` 标 pageNo 必填+补 pageSize+分页说明+未联调警示;`docs/openapi.yaml` drugs 内联 pageNo(required)+pageSize(不动共享 pageNo,网格地址仍可选)。 - 验证:openapi YAML 合法、drugs pageNo required=true、grid-addresses 共享 pageNo 仍 false;Go 静态自检通过;现有 drugs 测试(pageNo=1/pageNo=bad)不受影响。待用户 `go test ./...` 复验。 - 决策:只修请求侧分页契约(清晰的 bug);不扩 `Drug` 响应结构体(缺 ypxh/ypjl/ycjl),因 YPML00001 未真实联调、避免照 docx 猜类型,且 HTTP 走 Raw 不影响输出。全程注明 docx 契约待联调。 - 下一步:联调 YPML00001 拿真实分页样本后,确认 pageNo/pageSize 真契约并补 Drug 响应字段。