fix(osi): 校准机构查询请求契约

This commit is contained in:
ila
2026-07-13 20:02:19 +08:00
parent d60b979785
commit d30ff01c35
6 changed files with 44 additions and 13 deletions
+2 -1
View File
@@ -173,10 +173,11 @@
| 查询网格地址 | `/osi/api/auto/wgdzcx/query` | `WGDZ00001` | `parentCode`,`pageNo`,`operateUser` | `regionCode`,`regionName`,`isFamily`(层级) | | 查询网格地址 | `/osi/api/auto/wgdzcx/query` | `WGDZ00001` | `parentCode`,`pageNo`,`operateUser` | `regionCode`,`regionName`,`isFamily`(层级) |
| 责任医生查询 | `/osi/api/auto/zryscx/query` | `ZRYS00001` | `manaUnitId`,`operateUser` | `personId`,`personName` | | 责任医生查询 | `/osi/api/auto/zryscx/query` | `ZRYS00001` | `manaUnitId`,`operateUser` | `personId`,`personName` |
| 药品目录查询 | `/osi/api/auto/ypmlcx/query` | `YPML00001` | `pageNo`,`ypmc`,`pym` | `ypmc`,`ypdw`,`ypgg`,`jldw` | | 药品目录查询 | `/osi/api/auto/ypmlcx/query` | `YPML00001` | `pageNo`,`ypmc`,`pym` | `ypmc`,`ypdw`,`ypgg`,`jldw` |
| 机构查询 | `/osi/api/auto/cxjg/query` | `CXJG00002` | `organizCode`,`parentId` | `organizCode`,`organizName`,`organizType`,`parentId` | | 机构查询 | `/osi/api/auto/cxjg/query` | `CXJG00002` | `organizCode`,`parentId`(两个 key 均须上送,值可为空) | `organizCode`,`organizName`,`organizType`,`parentId`,`regionCode` |
> 公共服务接口是**基础字典服务**:网格地址→`regionCode`、责任医生→`personId`、机构→`organizCode`。 > 公共服务接口是**基础字典服务**:网格地址→`regionCode`、责任医生→`personId`、机构→`organizCode`。
> 它们正是创建类接口所需主数据(`regionCode`/`manaDoctorId`/`manaUnitId` 等)的来源,建议优先打通并本地缓存为字典。 > 它们正是创建类接口所需主数据(`regionCode`/`manaDoctorId`/`manaUnitId` 等)的来源,建议优先打通并本地缓存为字典。
> 机构查询实测发现:即使 `organizCode` 或 `parentId` 为空,两个 `baseInfo` key 仍必须发送;省略空字段会触发平台参数错误。`regionCode` 可能为 null。
--- ---
+3 -1
View File
@@ -235,10 +235,12 @@ checkId = 截断20位( 编码( 源系统标识 | dataType | 源记录主键 [ |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 网格地址查询 | `WGDZ00001` | `code="01"` 且返回数组非空 | 用已存在档案的区划/网格码派生 `parentCode` 候选值验证 | | 网格地址查询 | `WGDZ00001` | `code="01"` 且返回数组非空 | 用已存在档案的区划/网格码派生 `parentCode` 候选值验证 |
| 责任医生查询 | `ZRYS00001` | `code="01"` 且返回数组非空 | 入参 `manaUnitId` 使用档案返回的 9 位机构码 | | 责任医生查询 | `ZRYS00001` | `code="01"` 且返回数组非空 | 入参 `manaUnitId` 使用档案返回的 9 位机构码 |
| 机构查询 | `CXJG00002` | `code="01"` 且返回数组非空 | 入参 `organizCode` 使用档案返回的 9 位机构码 | | 机构查询 | `CXJG00002` | `code="01"` 且返回数组非空 | `baseInfo` 必须含 `organizCode` 和 `parentId` 两个 key(值可为空);响应 `regionCode` 可为 null |
| 药品目录查询 | `YPML00001` | 未做真实联调 | T-101 已完成客户端封装与单测;后续有药品关键字/拼音码时补样本 | | 药品目录查询 | `YPML00001` | 未做真实联调 | T-101 已完成客户端封装与单测;后续有药品关键字/拼音码时补样本 |
> T-103 字典缓存可以先依赖网格、责任医生、机构三类真实验证结果。药品目录与健康档案创建闭环无直接依赖,不阻塞 T-103/T-206。 > T-103 字典缓存可以先依赖网格、责任医生、机构三类真实验证结果。药品目录与健康档案创建闭环无直接依赖,不阻塞 T-103/T-206。
机构查询的 `OrgQuery` 不可为两个字段添加 `omitempty`:平台要求 `organizCode`、`parentId` 同时存在,即便查询只按其中一个条件过滤,另一个也应显式上送空字符串。机构响应的 `regionCode` 为所在网格码,平台可返回 null,Go 查询结构按空字符串承接。
--- ---
## 10. JKDA 创建/更新请求契约(T-206 本地基线) ## 10. JKDA 创建/更新请求契约(T-206 本地基线)
+4 -3
View File
@@ -162,11 +162,12 @@ GET /api/dictionaries/orgs
| 参数 | 必填 | 说明 | | 参数 | 必填 | 说明 |
| --- | --- | --- | | --- | --- | --- |
| `organizCode` | 否 | 机构编码 | | `organizCode` | 否 | 机构编码;HTTP 未传时仍会向 OSI 显式上送空字符串 |
| `parentId` | 否 | 上级机构 ID/编码 | | `parentId` | 否 | 上级机构 ID/编码;HTTP 未传时仍会向 OSI 显式上送空字符串 |
- 上游:`CXJG00002` `/auto/cxjg/query` - 上游:`CXJG00002` `/auto/cxjg/query`
- 返回:`data` 为**数组**(机构主数据,含 `organizCode`、`organizName`、`organizType`、`parentId` 等) - 返回:`data` 为**数组**(机构主数据,含 `organizCode`、`organizName`、`organizType`、`parentId`、`regionCode` 等);`regionCode` 可能为 null
- OSI 要求 `organizCode` 和 `parentId` 两个 `baseInfo` key 都存在,值可为空;本服务已保证该行为
### 10. 查询老年人生活自理能力评估 ### 10. 查询老年人生活自理能力评估
+4 -3
View File
@@ -401,7 +401,7 @@ paths:
tags: tags:
- dictionaries - dictionaries
summary: 查询机构 summary: 查询机构
description: 查询机构主数据,原样返回上游 CXJG00002 `/auto/cxjg/query` 响应。 description: 查询机构主数据,原样返回上游 CXJG00002 `/auto/cxjg/query` 响应。上游要求 organizCode 和 parentId 两个 baseInfo key 均存在;本服务会把未传参数显式上送为空字符串。
operationId: listOrgs operationId: listOrgs
parameters: parameters:
- $ref: '#/components/parameters/organizCode' - $ref: '#/components/parameters/organizCode'
@@ -423,6 +423,7 @@ paths:
organizName: 测试机构 organizName: 测试机构
organizType: '1' organizType: '1'
parentId: '441625' parentId: '441625'
regionCode: '441625000000'
'400': '400':
$ref: '#/components/responses/BadRequest' $ref: '#/components/responses/BadRequest'
'405': '405':
@@ -542,14 +543,14 @@ components:
name: organizCode name: organizCode
in: query in: query
required: false required: false
description: 机构编码。 description: 机构编码;HTTP 未传时服务仍向 OSI 显式上送空字符串。
schema: schema:
type: string type: string
parentId: parentId:
name: parentId name: parentId
in: query in: query
required: false required: false
description: 上级机构 ID/编码。 description: 上级机构 ID/编码;HTTP 未传时服务仍向 OSI 显式上送空字符串。
schema: schema:
type: string type: string
responses: responses:
+6 -3
View File
@@ -39,15 +39,18 @@ type Drug struct {
} }
type OrgQuery struct { type OrgQuery struct {
OrganizCode string `json:"organizCode,omitempty"` // docx 二者均标必填;联调验证通过的请求是两 key 都在(organizCode + parentId 可为 "")。
ParentID string `json:"parentId,omitempty"` // 不加 omitempty,确保 parentId="" 也上送,避免像网格地址那样必填空值被丢导致平台 NPE(code=02)。
OrganizCode string `json:"organizCode"`
ParentID string `json:"parentId"`
} }
type Org struct { type Org struct {
OrganizCode string `json:"organizCode"` OrganizCode string `json:"organizCode"`
OrganizName string `json:"organizName"` OrganizName string `json:"organizName"`
OrganizType string `json:"organizType"` OrganizType string `json:"organizType"` // A医院/B社区中心(站)/C卫生院/D门诊诊所村室/D6村卫生室/R市卫生局
ParentID string `json:"parentId"` ParentID string `json:"parentId"`
RegionCode string `json:"regionCode"` // 机构所在网格码;联调实测可为 null(→"")
} }
func (c *Client) QueryGridAddress(ctx context.Context, query GridAddressQuery) ([]GridAddress, Result, error) { func (c *Client) QueryGridAddress(ctx context.Context, query GridAddressQuery) ([]GridAddress, Result, error) {
+25 -2
View File
@@ -132,7 +132,7 @@ func TestQueryOrgsCallsCXJG00002(t *testing.T) {
if err := json.NewDecoder(r.Body).Decode(&seenPayload); err != nil { if err := json.NewDecoder(r.Body).Decode(&seenPayload); err != nil {
t.Fatalf("decode request: %v", err) t.Fatalf("decode request: %v", err)
} }
_, _ = w.Write([]byte(`{"code":"01","message":"操作成功","data":[{"organizCode":"441625001","organizName":"测试机构","organizType":"1","parentId":"441625"}]}`)) _, _ = w.Write([]byte(`{"code":"01","message":"操作成功","data":[{"organizCode":"441625001","organizName":"测试机构","organizType":"1","parentId":"441625","regionCode":"441625000000"}]}`))
})) }))
defer server.Close() defer server.Close()
@@ -157,7 +157,30 @@ func TestQueryOrgsCallsCXJG00002(t *testing.T) {
if baseInfo["organizCode"] != "441625001" || baseInfo["parentId"] != "441625" { if baseInfo["organizCode"] != "441625001" || baseInfo["parentId"] != "441625" {
t.Fatalf("baseInfo = %#v", baseInfo) t.Fatalf("baseInfo = %#v", baseInfo)
} }
if len(rows) != 1 || rows[0].OrganizCode != "441625001" || rows[0].OrganizName != "测试机构" || rows[0].OrganizType != "1" || rows[0].ParentID != "441625" { if len(rows) != 1 || rows[0].OrganizCode != "441625001" || rows[0].OrganizName != "测试机构" || rows[0].OrganizType != "1" || rows[0].ParentID != "441625" || rows[0].RegionCode != "441625000000" {
t.Fatalf("rows = %#v", rows) t.Fatalf("rows = %#v", rows)
} }
} }
func TestQueryOrgsSendsEmptyRequiredKeys(t *testing.T) {
var seenPayload map[string]any
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := json.NewDecoder(r.Body).Decode(&seenPayload); err != nil {
t.Fatalf("decode request: %v", err)
}
_, _ = w.Write([]byte(`{"code":"01","message":"操作成功","data":[]}`))
}))
defer server.Close()
client := newTestJKDAClient(t, server.URL)
if _, _, err := client.QueryOrgs(context.Background(), OrgQuery{}); err != nil {
t.Fatalf("QueryOrgs: %v", err)
}
baseInfo := seenPayload["uploadinfo"].(map[string]any)["baseInfo"].(map[string]any)
if _, ok := baseInfo["organizCode"]; !ok || baseInfo["organizCode"] != "" {
t.Fatalf("organizCode should be present and empty: %#v", baseInfo)
}
if _, ok := baseInfo["parentId"]; !ok || baseInfo["parentId"] != "" {
t.Fatalf("parentId should be present and empty: %#v", baseInfo)
}
}