Files
chis_osi/docs/04-字段与接口映射.md
T

24 KiB
Raw Blame History

04 · 字段与接口映射

本文是 mapping/ 与 contract/ 两层的落地指引:接口能力映射、PHIS→OSI 字段映射策略、码表清单、checkId 幂等键设计。


1. 接口能力 → 内部方法映射

OSI serviceId 路径 内部方法(osi 包) dataType(pipeline 分流)
JKDA00001 /jkda/create CreateHealthRecord health_record
JKDA00003 /jkda/update UpdateHealthRecord health_record
JKDA00002 /auto/jkda/find FindHealthRecord (查询)
JKDA00005 /auto/jkda/findrqbj FindRqbj (查询)
JKTJ00001 /jktj/create CreateHealthCheck health_check
JKTJ00003 /jktj/update UpdateHealthCheck health_check
JKTJ00002 /auto/jktj/query QueryHealthCheck (查询)
JKTJLIST00002 /auto/jktjlist/query ListHealthCheckPeople (查询)
JKTJLSJL00002 /auto/jktjlscx/query LastHealthCheck (查询)
LNRZLPG00001 /lnrzlpg/create CreateElderlySelfCare elderly_self_care
LNRZLPG00002 /auto/lnr/query QueryElderlySelfCare (查询,代码完成,待联调确认)
LNRZLPG00003 /lnrzlpg/update UpdateElderlySelfCare elderly_self_care
LNRZYTZ00001 /lnrzyygl/create CreateTCMConstitution tcm
LNRZYTZ00003 /lnrzyygl/update UpdateTCMConstitution tcm
(待确认) /auto/zyjkzd/create SaveTCMGuidance tcm_guidance
(待确认) /auto/zyjkzd/update UpdateTCMGuidance tcm_guidance
WGDZ00001 /auto/wgdzcx/query QueryGridAddress (字典)
ZRYS00001 /auto/zryscx/query QueryDoctors (字典)
YPML00001 /auto/ypmlcx/query QueryDrugs (字典)
CXJG00002 /auto/cxjg/query QueryOrgs (字典)

⚠ "(待确认)"项以 01 文档第 7 节为准,联调回填后更新 osi/codes.go 的 serviceId 常量与 pathOf 路由表。


2. 字段映射策略

2.1 三类字段,三种处理

  1. 直传字段:PHIS 与 OSI 同义且同形(如姓名、出生日期 yyyy-MM-dd)。直接拷贝,做必填/长度校验。
  2. 码表字段:枚举值需经字典转换(性别、民族、血型、职业…)。统一走 dict.go 双向查表,未命中报 ValidationError,绝不静默置空。
  3. 结构/多选字段:嵌套节点(既往史/家族史/手术/外伤/输血/生活环境)、逗号拼接多选。集中在各业务域映射函数里组装。

2.2 映射函数约定

// 返回结构化校验错误,pipeline 在投递前据此拦截,避免把脏数据打到平台。
func MapHealthRecord(src phis.HealthRecord, ctx MapContext) (contract.HealthRecordCreate, []ValidationError)
  • MapContext 携带机构级常量:orgCode/userName/operateUser/regionCode 默认值与字典快照。
  • 校验项:必填(idCard/personName/sexCode/birthday/mobileNumber/regionCode/manaDoctorId/manaUnitId…)、长度上限、码表合法性、日期格式。
  • 映射是纯函数,便于用 01 文档样例 + 联调真实样本做单测(对齐旧项目"可测试改动补最小必要测试")。

3. 码表清单(dict.go 初始内容)

取值来自 docx 字段说明,建模时全部落为常量表,并加注释标"来源:OSI 文档 §字段名"。

维度 取值(节选)
性别 sexCode 0 未知 / 1 男 / 2 女 / 9 未说明
户籍 registeredPermanent 1 户籍 / 2 非户籍
血型 bloodTypeCode 1 A / 2 B / 3 O / 4 AB / 5 不详
RH rhBloodCode 1 阳性 / 2 阴性 / 3 不详
文化程度 educationCode 10 研究生 / 20 本科 / 31 大专 / 41 中专 / 47 技校 / 60 高中 / 70 初中 / 80 小学 / 90 文盲 / 91 不详
职业 workCode 0 负责人 / 1·2 专技 / 3 办事 / 4 商业服务 / 5 农林牧渔 / 9-9 生产运输 / X 军人 / Y 其他 / 8 无职业
婚姻 maritalStatusCode 10 未婚 / 20 已婚 / 30 丧偶 / 40 离婚 / 90 未说明
医保 insuranceCode(多选) 01 城镇职工 / 02 城乡居民 / 04 贫困救助 / 05 商业 / 06 全公费 / 07 全自费 / 99 其他
民族 nationCode 01 汉 … 56 基诺 / 99 其他(56 项全表)
人群标记 personSign PU 普通 / GRQY 已签约 / LAO 老年 / GAO 高血压 / TANG 糖尿病 / FU 孕产妇 / ER 儿童 / FEI 肺结核 / JING 精神 / CAN 残疾
药物过敏 ywgms(多选) 0101 无 / 0102 青霉素 / 0103 磺胺 / 0104 链霉素 / 0109 其他
既往疾病 jwsjbcode 0201 无 / 0202 高血压 / 0203 糖尿病 / … / 0299 其他
残疾 cjqk(多选) 1101 无 / 1102 视力 / 1103 听力 / 1104 言语 / 1105 肢体 / 1106 智力 / 1107 精神 / 1108 孤独症 / 1109 脑瘫 / 1199 其他
厨房排风 cookAirTool 1 无 / 2 油烟机 / 3 换气扇 / 4 烟囱 / 9 其他
燃料 fuelType 1 液化气 / 2 煤 / 3 天然气 / 4 沼气 / 5 柴火 / 9 其他
饮水 waterSourceCode 1 自来水 / 2 净化水 / 3 井水 / 4 河湖水 / 5 塘水 / 9 其他
厕所 washroom 1 卫生厕所 / 2 粪池式 / 3 马桶 / 4 露天粪坑 / 5 简易棚厕 / 6 其他
机构类型 organizType A 医院 / B 社区中心(站) / C 卫生院 / D 门诊诊所村室 / D6 村卫生室 / R 市卫生局

多选字段统一用英文逗号拼接(文档示例如 "0102,0103")。注意 docx 个别返回样例用了中文逗号,解析时两者都要兼容。


4. 主数据依赖:字典服务先行

创建类接口的若干必填字段依赖公共服务查询结果,建议在投递前用字典缓存解析:

regionCode  ← QueryGridAddress(parentCode)    // 网格地址
manaDoctorId/operateUser ← QueryDoctors(manaUnitId)  // 责任医生
manaUnitId/organizCode   ← QueryOrgs(...)            // 机构

策略:启动或定时拉取这些字典,缓存到 cache.DictionarySnapshot(内存为必选,持久化接口可接 Redis)。映射时通过 MapContext.MasterData 按 PHIS 的地址/医生/机构名称反查 OSI 码:RegionCodeByName、DoctorIDByName、ManaUnitIDByName。命中失败仍表现为必填校验错误,不投递;Redis/持久化失败不阻断内存快照刷新。


5. checkId 与幂等键设计

checkId(第三方业务唯一识别码,长度 20)是新项目幂等的核心。

5.1 生成规则(mapping/checkid.go)

要求确定性:同一源记录无论重试多少次,都得到同一 checkId。

checkId = 截断20位( 编码( 源系统标识 | dataType | 源记录主键 [ | 版本/更新时间] ) )
  • 同一条源记录的"创建"应使用稳定 checkId → 平台据此识别为同一档案,避免重复建档。
  • "更新"沿用原 checkId(走 update 接口)。
  • 若源记录内容变更需视为新版本上送,可把"更新时间/版本号"纳入 checkId 计算——取决于平台是否以 checkId 去重(见 03 文档开放问题 5)。

5.2 幂等存储

  • 键:checkId(替代旧项目 account|idCard|dataType|sha1(saveBody))。
  • 值:{status: success|failed, phrId, lastTs, attempts}。
  • 命中 success:跳过,不重复投递。
  • failed:允许后续重试。
  • 存储:redis 优先、本地 JSON 降级(沿用旧项目 idempotency_store 思路)。

用 checkId 做幂等键的好处:与平台对账口径一致(平台也认 checkId),且不受报文字段微调影响。


6. 校验与错误分类(与 pipeline 配合)

成功码以 docs/01 §1 为准:实测为字符串 "01"(非 "1"),判定按去前导零后 == "1"。

错误来源 分类 处理
映射缺必填/码表未命中/格式非法 不可重试(数据错) 直接 failed,回写 PHIS,附 ValidationError 明细
网络错误 / SOCKS / EOF / 超时 可重试 退避重试,计熔断
OSI code == "405"(服务超时) 可重试 退避重试
OSI code 非成功码非 405(业务/权限拒绝) 不可重试 failed,记 message 供排查
HTTP 5xx / 429 可重试 退避重试

7. 待补字段表

以下接口的完整字段表 docx 未充分给出或存在坑点,建模时以联调样本为准并在此登记:

  • 体检 jktj/create 全量字段(hcData/lsData/exaData/aeData,体量最大)
  • 老年人自理评估 lnrzlpg 查询字段 —— 厂家查询文档已给出,待真实联调确认,见 §13
  • 老年人自理评估 lnrzlpg 创建/更新字段
  • 中医体质辨识 lnrzyygl 字段
  • 中医健康指导 zyjkzd 字段 + serviceId
  • 个人档案查询 JKDA00002 的请求信封与返回结构 —— 已联调实测,见 §8
  • 其余列表/查询接口的分页与返回数组结构(体检/老年人/中医指导)

8. JKDA00002 个人档案查询 · 联调实测契约

来源:2026-07 沙箱一次成功查询(code="01" 操作成功)。下方均为脱敏结构,真实身份证/姓名/联系方式等 PII 只在本地样本,不入库。 这是 contract/ 里 FindHealthRecord 请求/响应结构体与 osi.FindHealthRecord 的事实基线。

8.1 请求(POST /osi/api/auto/jkda/find)

{
  "serviceId": "JKDA00002",
  "uploadinfo": {
    "baseInfo":   { "idCard": "<idCard>" },
    "manageInfo": { "DSFMC": "<userName>", "operateUser": "<operateUser>", "operateUnit": "<orgCode-18位>" }
  }
}
  • baseInfo 三选一:idCard / phrid / personName。
  • manageInfo 三字段来自机构级常量(见 docs/01 §3 三种机构码区分)。
  • 请求头另带 orgCode(=operateUnit) / deviceSN(查询可空) / ts / userName(=DSFMC) / password,签名规则见 docs/01 §2。

8.2 响应(code="01",data 为数组,每元素 = 一份档案聚合)

节点 类型 说明
healthRecord object 档案主体(人口学 + 管理字段),见 8.3
pastHistory object 既往史标志位:ywgms(药物过敏)/jwsjb标志/bls/ycbs(遗传病)/cjqk(残疾)/jzsfqn/jzsmq/jzszn/jzsxdjm(家族史·父/母/子女/兄弟姐妹) 等 code
jwsjb array 既往疾病:{ jwsjbcode, jwsjbmc, jwsjbqzsj(确诊时间) }
jwsss array 既往手术:{ jwssscode, jwsssmc, jwsssqzsj }
jwsws array 既往外伤:{ jwswscode, jwswsmc }
jwssx array 既往输血:{ jwssxcode, jwssxyy(原因) }
familyMiddle object 生活环境:waterSourceCode/fuelType/cookAirTool/washroom/livestockColumn/isFillShhj

pastHistory/familyMiddle 实测是 object(非 docx 查询侧标的 list);多选标志仍以码值出现。

8.3 healthRecord 关键字段(实测字段名,注意坑点拼写)

字段 语义 备注
idCard personName sexCode birthday 人口学主键四要素 直传+校验
phrId 健康档案号 注意驼峰 phrId(查询入参用小写 phrid)
empiId EMPI 主索引 32 位十六进制串
checkId 第三方业务唯一码 本样本为 null(存量档案无 checkId)→ 佐证幂等键需我方生成,见 §5
manaUnitId createUnit 管辖/建档机构 9 位机构码,≠ 请求头 orgCode(18位)
manaDoctorId createUser 责任医生/建档人 机构内人员 ID
regionCode addressCode homePlaceCode 行政区划/网格码 12 位
address adressNumber 现住址/门牌号 ⚠门牌号字段名少一个 d:adressNumber
homePlace homePlaceNumber 户籍地/门牌
nationCode educationCode workCode maritalStatusCode bloodTypeCode rhBloodCode insuranceCode 码表字段 走 dict.go 双向查表(§3)
personGroup otherPersonGroup 人群标记 与 JKDA00005 personSign 联动
isFillShhj 是否填生活环境 y/n
contact contactPhone mobileNumber phoneNumber 联系人/电话 PII
status createDate 档案状态/建档日期

建模提示:data 用数组承接(可能多档案/多版本);insuranceType、otherPersonGroup、checkId 等实测可为 null,结构体用指针或 omitempty,映射层不得因 null 报错。


9. 公共字典查询 · 联调实测契约

来源:2026-07-07 沙箱验证。使用本地未入库凭据和一条已存在档案反查主数据后验证;下方只记录脱敏事实,不记录真实身份证、机构码、医生姓名或密钥。

9.1 请求信封

四个公共查询接口与 JKDA 查询一致,仍走统一信封:

{
  "serviceId": "<公共查询serviceId>",
  "uploadinfo": {
    "baseInfo": { "...": "..." },
    "manageInfo": { "DSFMC": "<userName>", "operateUser": "<operateUser>", "operateUnit": "<orgCode-18位>" }
  }
}

9.2 已验证结果

接口 serviceId 真实验证结论 备注
网格地址查询 WGDZ00001 code="01" 且返回数组非空 用已存在档案的区划/网格码派生 parentCode 候选值验证
责任医生查询 ZRYS00001 code="01" 且返回数组非空 入参 manaUnitId 使用档案返回的 9 位机构码
机构查询 CXJG00002 code="01" 且返回数组非空 baseInfo 必须含 organizCode 和 parentId 两个 key(值可为空);响应 regionCode 可为 null
药品目录查询 YPML00001 未做真实联调 T-101 已完成客户端封装与单测;后续有药品关键字/拼音码时补样本

T-103 字典缓存可以先依赖网格、责任医生、机构三类真实验证结果。药品目录与健康档案创建闭环无直接依赖,不阻塞 T-103/T-206。

机构查询的 OrgQuery 不可为两个字段添加 omitempty:平台要求 organizCode、parentId 同时存在,即便查询只按其中一个条件过滤,另一个也应显式上送空字符串。机构响应的 regionCode 为所在网格码,平台可返回 null,Go 查询结构按空字符串承接。

10. JKDA 创建/更新请求契约(T-206 本地基线)

T-206 已完成本地契约与客户端方法并通过单测;真实写入验收拆为 T-215,当前等待明确写入授权与安全测试档案。

  • 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。

T-215 真实 create/update 联调完成后,需要在本节补回平台返回样本、phrId 字段确认、同一 checkId 重复提交的幂等语义、更新目标标识、addressNumber 落库结果及逐档案 operateUser 规则。


11. JKTJ 健康体检查询 · 联调实测契约(T-301)

来源:2026-07 沙箱"最近一次体检"一次成功查询(code="01")。脱敏结构,真实值只在本地样本、不入库。 这是体检查询的事实基线,也是将来体检 create 映射的字段清单来源。

11.1 接口与状态

业务 serviceId 路径 返回 状态
最近一次体检 JKTJLSJL00002 /auto/jktjlscx/query 单对象(最近一次完整体检) ✅ 实测可用(osi.LastHealthCheck)
某人全部体检 JKTJ00002 /auto/jktj/query 数组(该人全部体检,每条完整) ✅ 实测可用(osi.QueryHealthChecks;早期曾报"没有url配置",现已部署)
已检/待检名单 JKTJLIST00002 /auto/jktjlist/query 数组(人员名单+状态,非体检明细) ✅ 实测可用(osi.ListHealthCheckPeople,见 §11.4)
  • 请求信封同档案(serviceId + uploadinfo{baseInfo, manageInfo});查询按 phrid/idCard 之一(docx baseInfo 只列这俩)。
  • 身份证字段大小写三接口不一致(务必分开建模):最近一次 JKTJLSJL00002 用小写 idcard;JKTJ00002 与 JKTJLIST00002 用**驼峰 idCard``**。档案 find 也是 idCard`。
  • 最近一次的 data 是单个对象(JKTJ00002 与档案 find 是数组)。
  • JKTJ00002 返回形态(实测):data 为数组=该人全部体检历史(本样本 8 条,2013–2026);每个元素是一份完整体检,结构同"最近一次"(38 顶层标量 + healthAssessment/examination/lifestySituation/accessoryExamination + 3 数组节点,见 §11.3)。
    • ⚠ checkId 仅近年记录有值,历史记录为 null(本样本仅最近 2 条有体检编号,更早 6 条 checkId=null)——第三方流水码是接入后才有的。
    • 因此"按体检编号取历史某次"只对有 checkId 的近期记录成立;更早的体检没有 checkId,只能按 checkDate 定位。
    • docx 无"按 checkId 直接查单条"的接口——查历史须用本接口取该人全部体检,再本地按 checkId/checkDate 过滤。

11.2 记录结构与主键

  • 体检记录主键 = checkId(体检自身流水,注意与档案 §5 我方生成的幂等 checkId 同名不同义)。
  • 各子节点用 healthCheck 外键指向主键(值 = checkId);子节点各有自己的 id:healthAssessment.assessmentId、accessoryExamination.recordId、inhospitalSituations[].situationId。

11.3 字段清单(实测约 260 项,供体检 create 建模用)

查询侧只强类型化定位字段(contract.HealthCheckSummary:checkId/healthCheck/idcard/personName/checkDate),完整内容由 osi.Result.Raw 保留。 全量字段的强类型建模留到体检 create 任务——写入映射才需要逐字段建模,届时以本清单 + 联调 create 样本为准,不照 docx 猜类型(本样本大量字段为 null,类型不可判)。

节点 类型 字段数 说明
(顶层标量) - 39 体征与主诉:身高体重 height/weight/bmi(float)、血压 constriction/diastolic(_l)、temperature/pulse/breathe/waistline、symptom/healthstatus/selfcare/cognitive/emotion 及各类疾病标志
healthAssessment object 24 健康评价:abnormality1..8、riskfactorsControl、assessmentId + create/lastModify 审计字段
examination object 52 一般查体:皮肤/淋巴结/心肺/腹部/肝脾/乳腺/妇科等 * + *Desc 描述对
lifestySituation object 43 生活方式:吸烟 wehtherSmoke/beginSmokeTime、饮酒 drinkingFrequency、运动、职业暴露 occupational/dust/ray/chemicals
accessoryExamination object 84 辅助检查(最大):血常规 wbc/hgb/platelet、生化 alt/ast/glu/fbs/hba1c/tc/tg/hdl/ldl/cr/bun/tbil、尿常规、心电 ecg、胸片 x、视力听力 recordId
inhospitalSituations array 19/项 住院/家庭病床:type、inhospitalDate/outhospitalDate、situationId,多含 *_text 中文回显
nonimmuneInoculations array - 非免疫规划预防接种(本样本空,结构待样本)
medicineSituations array - 用药情况(本样本空,结构待样本)

命名坑点:constriction=收缩压、diastolic=舒张压(_l 疑为左侧);辅助检查用大量医学缩写。建 create 映射时逐字段加中文注释。

11.4 已检/待检人员名单查询 JKTJLIST00002(docx 契约,沙箱部署待验证)

不是体检数据源,是机构级"某年度已检/未检人员名单/进度":返回人员一行 + 体检状态,不含体检明细。 来源:docx + 2026-07 沙箱实测(code="01",返回分页人员名单)。端点已部署可用(T-305)。

  • serviceId JKTJLIST00002,POST /osi/api/auto/jktjlist/query,信封同查询式(uploadinfo{baseInfo, manageInfo})。
  • 请求 baseInfo(page/rows 也在 baseInfo 内):
字段 必填 说明
checkYear ✅ 检查年度(如 2025)——无"默认所有年度"
page rows ✅ 分页,rows 默认 10
idCard 否 过滤到某人某年度状态
checkType 否 0已检 / 1未检 / 2全部
personName sex age personGroup gridAddress beginDate endDate 否 各类筛选。⚠ docx 参数表与样例字段名不一致(表 ageBegin/ageEnd/sexCode/regionCode vs 样例 age/sex/gridAddress)——以联调实测为准
  • 响应 data(list,每人一行)· 实测字段名(⚠ 与 docx 多处不符,以实测为准): idCard/personName/age/birthday(docx 误写 birthDay)/sexCode/regionCode/regionCodeText/manaUnitId/manaUnitText/manaDoctorId(docx 误写 manadoctorId)/manaDocterName(拼写 Docter)/phrId(docx 误写 phrid,此处驼峰)/empiId/signFlag(y/n 签约)/address/mobileNumber/contact/contactPhone/checkType(0已检/1未检)/rqbj(人群标志,同 personSign 码表)。

12. JKDA00005 人群分类查询 · 联调实测契约(T-209)

来源:2026-07-09 本地探针 scripts/query_crow_with_idcard.py 与返回样本 scripts/query_crow_with_idcard.json。样本含真实身份证和档案号,只本地留存、不入库;本文只记录脱敏结构。

12.1 请求

{
  "serviceId": "JKDA00005",
  "uploadinfo": {
    "baseInfo": { "idCard": "<idCard>" },
    "manageInfo": { "DSFMC": "<userName>", "operateUser": "<operateUser>", "operateUnit": "<orgCode-18位>" }
  }
}
  • 路径:/osi/api/auto/jkda/findrqbj。
  • baseInfo 支持 idCard / phrid 二选一;实测样本使用 idCard。
  • 注意:早期代码/文档登记为 /jkda/findrqbj,本次实测脚本使用 auto/jkda/findrqbj 返回 code="01",T-209 需同步修正 osi/codes.go。

12.2 响应

{
  "code": "01",
  "message": "操作成功",
  "data": {
    "personSign": "LAO",
    "idCard": "<idCard>",
    "phrId": "<phrId>"
  }
}

字段说明:

字段 类型 说明
personSign string 人群分类码,可能为逗号分隔多值;码表见 §3 personSign
idCard string 身份证号,PII
phrId string 健康档案号,响应为驼峰 phrId

Go 侧契约应从只承接 personSign 优化为完整承接 personSign/idCard/phrId,server 端点原样回写平台 JSON,避免裁掉后续可能新增字段。


13. 老年人生活自理能力评估查询 · 厂家文档契约(待联调确认)

来源:2026-07 新增《老年人生活自理能力评估查询服务.docx》。文档中 serviceId 参数表与请求样例冲突;以下以参数表的 LNRZLPG00002 为实现候选,真实请求成功后才能升级为实测契约。

13.1 请求

  • POST /osi/api/auto/lnr/query
  • serviceId: LNRZLPG00002
  • 信封沿用实测查询格式:{"serviceId", "uploadinfo": {"baseInfo", "manageInfo"}}。docx 样例遗漏了 uploadinfo/manageInfo,不可照搬。

baseInfo 字段:

字段 必填 说明
idCard ✅ 身份证件号
phrId 否 健康档案编号
checkId 否 第三方检查主键

13.2 响应

data 为评估记录数组。每条记录包含:

维度 原始值 等级 评分
进餐 jc jcdj jcpf
梳洗 sx sxdj sxpf
穿衣 cy cydj cypf
如厕 rc rcdj rcpf
活动 hd hddj hdpf
总评 zp zpdj zpfs
  • 等级统一为:1 可自理、2 轻度依赖、3 中度依赖、4 不能自理。
  • 定位和审计字段:phrId、checkId、createUnit、createUser、createDate、inputDate、inputUser、inputUnit。
  • docx 仍误写成功码为 "1";项目统一按现有 IsSuccessCode 兼容处理,实测成功基线仍为 "01"。
  • docx 请求样例错误写为 "serviceId":" LNR00004",不能用作实现依据。