feat(osi): 体检最近一次查询打通(T-301)
- osi.LastHealthCheck(JKTJLSJL00002 /auto/jktjlscx/query) - contract.HealthCheckSummary 只强类型化定位字段,完整内容走 Result.Raw - codes.go 登记 JKTJLSJL00002 + JKTJLIST00002 路由 - 单测:路径/解码/Raw 保留/idcard 小写映射/单键校验(脱敏假数据) - 实测:单查 JKTJ00002 平台未部署;data 为单对象;身份证字段小写 idcard Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
package contract
|
||||||
|
|
||||||
|
// 健康体检查询契约(JKTJ)。
|
||||||
|
//
|
||||||
|
// 体检记录字段极多(实测最近一次样本约 260 项,分 4 个 object 子节点 +
|
||||||
|
// 3 个 array 子节点,见 docs/04 §11)。查询场景只需定位/摘要字段即可,
|
||||||
|
// 完整内容由 osi.Result.Raw 原样保留;全量字段的强类型建模留到体检 create
|
||||||
|
// 任务(写入映射才真正需要每个字段),避免照单样本猜 null 字段类型。
|
||||||
|
//
|
||||||
|
// 注意与档案的两处差异:
|
||||||
|
// - 最近一次体检 data 是**单个对象**(档案 find 的 data 是数组)。
|
||||||
|
// - 体检身份证字段是小写 `idcard`(档案是 `idCard`)。
|
||||||
|
type HealthCheckSummary struct {
|
||||||
|
CheckID string `json:"checkId"` // 体检记录主键;各子节点用 healthCheck 外键指向它
|
||||||
|
HealthCheck string `json:"healthCheck"` // 冗余外键,值等于 checkId
|
||||||
|
IDCard string `json:"idcard"` // 体检用小写 idcard
|
||||||
|
PersonName string `json:"personName"`
|
||||||
|
CheckDate string `json:"checkDate"`
|
||||||
|
}
|
||||||
@@ -14,6 +14,11 @@ const (
|
|||||||
ServiceIDDoctors = "ZRYS00001"
|
ServiceIDDoctors = "ZRYS00001"
|
||||||
ServiceIDDrugs = "YPML00001"
|
ServiceIDDrugs = "YPML00001"
|
||||||
ServiceIDOrgs = "CXJG00002"
|
ServiceIDOrgs = "CXJG00002"
|
||||||
|
|
||||||
|
ServiceIDJKTJLast = "JKTJLSJL00002" // 最近一次体检(实测可用)
|
||||||
|
ServiceIDJKTJList = "JKTJLIST00002" // 已检/待检人员列表(入参待厂家确认)
|
||||||
|
// 单条查询 JKTJ00002:docx 标 auto/jktj/query,平台实测"没有url的接口配置"(未部署),
|
||||||
|
// auto/jktj/find 待确认;确认前不登记路由。见 docs/04 §11、docs/06 B5。
|
||||||
)
|
)
|
||||||
|
|
||||||
const codeRetryableTimeout = "405"
|
const codeRetryableTimeout = "405"
|
||||||
@@ -27,6 +32,8 @@ var servicePaths = map[string]string{
|
|||||||
ServiceIDDoctors: "/auto/zryscx/query",
|
ServiceIDDoctors: "/auto/zryscx/query",
|
||||||
ServiceIDDrugs: "/auto/ypmlcx/query",
|
ServiceIDDrugs: "/auto/ypmlcx/query",
|
||||||
ServiceIDOrgs: "/auto/cxjg/query",
|
ServiceIDOrgs: "/auto/cxjg/query",
|
||||||
|
ServiceIDJKTJLast: "/auto/jktjlscx/query",
|
||||||
|
ServiceIDJKTJList: "/auto/jktjlist/query",
|
||||||
}
|
}
|
||||||
|
|
||||||
func PathOf(serviceID string) (string, error) {
|
func PathOf(serviceID string) (string, error) {
|
||||||
|
|||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
package osi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"chis_osi/contract"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FindHealthCheckQuery struct {
|
||||||
|
IDCard string
|
||||||
|
PHRID string
|
||||||
|
EMPIID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastHealthCheck 查询某人最近一次体检(JKTJLSJL00002 /auto/jktjlscx/query)。
|
||||||
|
// data 为单个体检对象(非数组);summary 只取定位字段,完整内容见返回的 Result.Raw。
|
||||||
|
func (c *Client) LastHealthCheck(ctx context.Context, query FindHealthCheckQuery) (contract.HealthCheckSummary, Result, error) {
|
||||||
|
baseInfo, err := query.baseInfo()
|
||||||
|
if err != nil {
|
||||||
|
return contract.HealthCheckSummary{}, Result{}, err
|
||||||
|
}
|
||||||
|
var summary contract.HealthCheckSummary
|
||||||
|
result, err := c.Call(ctx, ServiceIDJKTJLast, baseInfo, &summary)
|
||||||
|
return summary, result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q FindHealthCheckQuery) baseInfo() (map[string]string, error) {
|
||||||
|
fields := []queryField{
|
||||||
|
{name: "idCard", value: q.IDCard},
|
||||||
|
{name: "empiId", value: q.EMPIID},
|
||||||
|
{name: "phrid", value: q.PHRID},
|
||||||
|
}
|
||||||
|
return singleBaseInfo("last health check", fields)
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package osi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLastHealthCheckDecodesSummaryAndHitsPath(t *testing.T) {
|
||||||
|
var seenPath string
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
seenPath = r.URL.Path
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
// data 为单个对象(非数组);字段名取自实测结构,值均为脱敏假数据
|
||||||
|
_, _ = w.Write([]byte(`{"code":"01","message":"操作成功","data":{"checkId":"CHK-TEST-001","healthCheck":"CHK-TEST-001","idcard":"TEST-ID","personName":"测试","checkDate":"2026-01-01"}}`))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
transport, err := NewTransport(TransportConfig{Timeout: time.Second})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewTransport: %v", err)
|
||||||
|
}
|
||||||
|
client := NewClient(ClientConfig{BaseURL: server.URL, UserName: "u", Ask: "k", Transport: transport})
|
||||||
|
|
||||||
|
summary, result, err := client.LastHealthCheck(context.Background(), FindHealthCheckQuery{IDCard: "TEST-ID"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LastHealthCheck: %v", err)
|
||||||
|
}
|
||||||
|
if seenPath != "/osi/api/auto/jktjlscx/query" {
|
||||||
|
t.Fatalf("path = %q", seenPath)
|
||||||
|
}
|
||||||
|
if summary.CheckID != "CHK-TEST-001" || summary.HealthCheck != "CHK-TEST-001" {
|
||||||
|
t.Fatalf("summary keys = %#v", summary)
|
||||||
|
}
|
||||||
|
// 体检身份证是小写 idcard,确认 tag 正确映射
|
||||||
|
if summary.IDCard != "TEST-ID" {
|
||||||
|
t.Fatalf("summary.IDCard = %q (体检 idcard tag 映射错误)", summary.IDCard)
|
||||||
|
}
|
||||||
|
if !result.Success || len(result.Raw) == 0 {
|
||||||
|
t.Fatalf("result should be success with raw retained: %#v", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindHealthCheckQueryRequiresExactlyOneKey(t *testing.T) {
|
||||||
|
if _, err := (FindHealthCheckQuery{}).baseInfo(); err == nil {
|
||||||
|
t.Fatal("empty query should error")
|
||||||
|
}
|
||||||
|
if _, err := (FindHealthCheckQuery{IDCard: "a", PHRID: "b"}).baseInfo(); err == nil {
|
||||||
|
t.Fatal("two keys should error")
|
||||||
|
}
|
||||||
|
bi, err := (FindHealthCheckQuery{IDCard: "a"}).baseInfo()
|
||||||
|
if err != nil || bi["idCard"] != "a" {
|
||||||
|
t.Fatalf("baseInfo = %v err = %v", bi, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user