feat(osi): 实现 JKDA 档案 Create/Update 与创建请求契约(T-206)
- contract/jkda.go 补创建请求结构体(healthRecord + manageInfo + 既往史节点) - osi/jkda.go 新增 CreateHealthRecord/UpdateHealthRecord - osi/codes.go 登记 JKDA00001 create / JKDA00003 update serviceId 与路径 - mapping 映射创建路径(真实字典快照反查主数据) - 代码与单测完成;真实 create 待安全测试档案与写入授权(见 tasks.md T-206 BLOCKED) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -97,3 +97,49 @@ type FamilyMiddle struct {
|
|||||||
LivestockColumn string `json:"livestockColumn,omitempty"`
|
LivestockColumn string `json:"livestockColumn,omitempty"`
|
||||||
IsFillSHHJ string `json:"isFillShhj,omitempty"`
|
IsFillSHHJ string `json:"isFillShhj,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HealthRecordCreate struct {
|
||||||
|
BaseInfo HealthRecordBaseInfo `json:"baseInfo,omitempty"`
|
||||||
|
ManageInfo ManageInfo `json:"manageInfo"`
|
||||||
|
HealthRecord HealthRecordCreateInfo `json:"healthRecord"`
|
||||||
|
PastHistory *PastHistory `json:"pastHistory,omitempty"`
|
||||||
|
PastDiseases []PastDisease `json:"jwsjb,omitempty"`
|
||||||
|
PastSurgeries []PastSurgery `json:"jwsss,omitempty"`
|
||||||
|
PastTraumas []PastTrauma `json:"jwsws,omitempty"`
|
||||||
|
PastTransfusions []PastBloodTx `json:"jwssx,omitempty"`
|
||||||
|
FamilyMiddle *FamilyMiddle `json:"familyMiddle,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HealthRecordBaseInfo struct {
|
||||||
|
IDCard string `json:"idCard,omitempty"`
|
||||||
|
PersonName string `json:"personName,omitempty"`
|
||||||
|
PHRID string `json:"phrId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HealthRecordCreateInfo struct {
|
||||||
|
CheckID string `json:"checkId,omitempty"`
|
||||||
|
IDCard string `json:"idCard,omitempty"`
|
||||||
|
PersonName string `json:"personName,omitempty"`
|
||||||
|
SexCode string `json:"sexCode,omitempty"`
|
||||||
|
Birthday string `json:"birthday,omitempty"`
|
||||||
|
PhrID string `json:"phrId,omitempty"`
|
||||||
|
MobileNumber string `json:"mobileNumber,omitempty"`
|
||||||
|
RegionCode string `json:"regionCode,omitempty"`
|
||||||
|
ManaDoctorID string `json:"manaDoctorId,omitempty"`
|
||||||
|
ManaUnitID string `json:"manaUnitId,omitempty"`
|
||||||
|
CreateUser string `json:"createUser,omitempty"`
|
||||||
|
CreateUnit string `json:"createUnit,omitempty"`
|
||||||
|
NationCode string `json:"nationCode,omitempty"`
|
||||||
|
EducationCode string `json:"educationCode,omitempty"`
|
||||||
|
WorkCode string `json:"workCode,omitempty"`
|
||||||
|
MaritalStatusCode string `json:"maritalStatusCode,omitempty"`
|
||||||
|
BloodTypeCode string `json:"bloodTypeCode,omitempty"`
|
||||||
|
RhBloodCode string `json:"rhBloodCode,omitempty"`
|
||||||
|
InsuranceCode string `json:"insuranceCode,omitempty"`
|
||||||
|
AddressNumber string `json:"addressNumber,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HealthRecordSaveResult struct {
|
||||||
|
PhrID string `json:"phrId"`
|
||||||
|
CreateUnit string `json:"createUnit,omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -135,3 +135,47 @@ func TestFindHealthRecordResponseDeserializesDataArray(t *testing.T) {
|
|||||||
t.Fatalf("checkId = %#v, want nil", response.Data[0].HealthRecord.CheckID)
|
t.Fatalf("checkId = %#v, want nil", response.Data[0].HealthRecord.CheckID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func TestHealthRecordCreateSerializesCreateFieldNames(t *testing.T) {
|
||||||
|
req := HealthRecordCreate{
|
||||||
|
BaseInfo: HealthRecordBaseInfo{
|
||||||
|
IDCard: "440000********1234",
|
||||||
|
PersonName: "测试人员",
|
||||||
|
},
|
||||||
|
HealthRecord: HealthRecordCreateInfo{
|
||||||
|
CheckID: "CHK2026070700000001",
|
||||||
|
IDCard: "440000********1234",
|
||||||
|
PersonName: "测试人员",
|
||||||
|
SexCode: "1",
|
||||||
|
Birthday: "1970-01-01",
|
||||||
|
MobileNumber: "13900000000",
|
||||||
|
RegionCode: "441625000000",
|
||||||
|
ManaDoctorID: "doc-001",
|
||||||
|
ManaUnitID: "123456789",
|
||||||
|
AddressNumber: "101号",
|
||||||
|
},
|
||||||
|
PastHistory: &PastHistory{YWGMS: "0101"},
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal: %v", err)
|
||||||
|
}
|
||||||
|
var got map[string]any
|
||||||
|
if err := json.Unmarshal(raw, &got); err != nil {
|
||||||
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
healthRecord := got["healthRecord"].(map[string]any)
|
||||||
|
if healthRecord["checkId"] != "CHK2026070700000001" {
|
||||||
|
t.Fatalf("healthRecord = %#v", healthRecord)
|
||||||
|
}
|
||||||
|
if healthRecord["addressNumber"] != "101号" {
|
||||||
|
t.Fatalf("addressNumber missing: %#v", healthRecord)
|
||||||
|
}
|
||||||
|
if _, ok := healthRecord["adressNumber"]; ok {
|
||||||
|
t.Fatalf("create request used response typo adressNumber: %#v", healthRecord)
|
||||||
|
}
|
||||||
|
baseInfo := got["baseInfo"].(map[string]any)
|
||||||
|
if baseInfo["idCard"] != "440000********1234" || baseInfo["personName"] != "测试人员" {
|
||||||
|
t.Fatalf("baseInfo = %#v", baseInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package mapping
|
package mapping
|
||||||
|
|
||||||
|
import "chis_osi/contract"
|
||||||
|
|
||||||
type PHISHealthRecord struct {
|
type PHISHealthRecord struct {
|
||||||
SourceSystem string
|
SourceSystem string
|
||||||
SourceRecordID string
|
SourceRecordID string
|
||||||
@@ -158,3 +160,31 @@ func mapCode(dict Dict, code string, errs *[]ValidationError) string {
|
|||||||
}
|
}
|
||||||
return mapped
|
return mapped
|
||||||
}
|
}
|
||||||
|
func BuildHealthRecordCreate(mapped MappedHealthRecord) contract.HealthRecordCreate {
|
||||||
|
return contract.HealthRecordCreate{
|
||||||
|
BaseInfo: contract.HealthRecordBaseInfo{
|
||||||
|
IDCard: mapped.IDCard,
|
||||||
|
PersonName: mapped.PersonName,
|
||||||
|
},
|
||||||
|
HealthRecord: contract.HealthRecordCreateInfo{
|
||||||
|
CheckID: mapped.CheckID,
|
||||||
|
IDCard: mapped.IDCard,
|
||||||
|
PersonName: mapped.PersonName,
|
||||||
|
SexCode: mapped.SexCode,
|
||||||
|
Birthday: mapped.Birthday,
|
||||||
|
MobileNumber: mapped.MobileNumber,
|
||||||
|
RegionCode: mapped.RegionCode,
|
||||||
|
ManaDoctorID: mapped.ManaDoctorID,
|
||||||
|
ManaUnitID: mapped.ManaUnitID,
|
||||||
|
CreateUser: mapped.ManaDoctorID,
|
||||||
|
CreateUnit: mapped.ManaUnitID,
|
||||||
|
NationCode: mapped.NationCode,
|
||||||
|
EducationCode: mapped.EducationCode,
|
||||||
|
WorkCode: mapped.WorkCode,
|
||||||
|
MaritalStatusCode: mapped.MaritalStatusCode,
|
||||||
|
BloodTypeCode: mapped.BloodTypeCode,
|
||||||
|
RhBloodCode: mapped.RhBloodCode,
|
||||||
|
InsuranceCode: mapped.InsuranceCode,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,26 @@ package mapping
|
|||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
|
func TestBuildHealthRecordCreateUsesMappedFieldsAndCheckID(t *testing.T) {
|
||||||
|
mapped, errs := MapHealthRecord(validPHISHealthRecord(), MapContext{Dicts: DefaultDictSnapshot()})
|
||||||
|
if len(errs) != 0 {
|
||||||
|
t.Fatalf("errors = %#v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
create := BuildHealthRecordCreate(mapped)
|
||||||
|
if create.HealthRecord.CheckID != mapped.CheckID {
|
||||||
|
t.Fatalf("checkId = %q, want %q", create.HealthRecord.CheckID, mapped.CheckID)
|
||||||
|
}
|
||||||
|
if create.HealthRecord.IDCard != mapped.IDCard || create.HealthRecord.PersonName != mapped.PersonName {
|
||||||
|
t.Fatalf("healthRecord demographics = %#v", create.HealthRecord)
|
||||||
|
}
|
||||||
|
if create.HealthRecord.RegionCode != mapped.RegionCode || create.HealthRecord.ManaDoctorID != mapped.ManaDoctorID || create.HealthRecord.ManaUnitID != mapped.ManaUnitID || create.HealthRecord.CreateUser != mapped.ManaDoctorID || create.HealthRecord.CreateUnit != mapped.ManaUnitID {
|
||||||
|
t.Fatalf("management fields = %#v", create.HealthRecord)
|
||||||
|
}
|
||||||
|
if create.BaseInfo.IDCard != mapped.IDCard || create.BaseInfo.PersonName != mapped.PersonName {
|
||||||
|
t.Fatalf("baseInfo = %#v", create.BaseInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestMapHealthRecordMapsDirectFieldsDictsAndCheckID(t *testing.T) {
|
func TestMapHealthRecordMapsDirectFieldsDictsAndCheckID(t *testing.T) {
|
||||||
src := validPHISHealthRecord()
|
src := validPHISHealthRecord()
|
||||||
got, errs := MapHealthRecord(src, MapContext{Dicts: DefaultDictSnapshot()})
|
got, errs := MapHealthRecord(src, MapContext{Dicts: DefaultDictSnapshot()})
|
||||||
|
|||||||
+14
-9
@@ -49,6 +49,18 @@ func NewClient(config ClientConfig) *Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Call(ctx context.Context, serviceID string, baseInfo any, out any) (Result, error) {
|
func (c *Client) Call(ctx context.Context, serviceID string, baseInfo any, out any) (Result, error) {
|
||||||
|
uploadInfo := contract.UploadInfo{
|
||||||
|
BaseInfo: baseInfo,
|
||||||
|
ManageInfo: contract.ManageInfo{
|
||||||
|
DSFMC: c.config.UserName,
|
||||||
|
OperateUser: c.config.OperateUser,
|
||||||
|
OperateUnit: c.operateUnit(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return c.callUploadInfo(ctx, serviceID, uploadInfo, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) callUploadInfo(ctx context.Context, serviceID string, uploadInfo any, out any) (Result, error) {
|
||||||
if c.transport == nil {
|
if c.transport == nil {
|
||||||
transport, err := NewTransport(TransportConfig{})
|
transport, err := NewTransport(TransportConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -63,15 +75,8 @@ func (c *Client) Call(ctx context.Context, serviceID string, baseInfo any, out a
|
|||||||
}
|
}
|
||||||
|
|
||||||
payload := contract.Envelope{
|
payload := contract.Envelope{
|
||||||
ServiceID: serviceID,
|
ServiceID: serviceID,
|
||||||
UploadInfo: contract.UploadInfo{
|
UploadInfo: uploadInfo,
|
||||||
BaseInfo: baseInfo,
|
|
||||||
ManageInfo: contract.ManageInfo{
|
|
||||||
DSFMC: c.config.UserName,
|
|
||||||
OperateUser: c.config.OperateUser,
|
|
||||||
OperateUnit: c.operateUnit(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
targetURL := c.urlFor(path)
|
targetURL := c.urlFor(path)
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
ServiceIDJKDACreate = "JKDA00001"
|
||||||
ServiceIDJKDAFind = "JKDA00002"
|
ServiceIDJKDAFind = "JKDA00002"
|
||||||
|
ServiceIDJKDAUpdate = "JKDA00003"
|
||||||
ServiceIDJKDAFindRqbj = "JKDA00005"
|
ServiceIDJKDAFindRqbj = "JKDA00005"
|
||||||
ServiceIDGridAddress = "WGDZ00001"
|
ServiceIDGridAddress = "WGDZ00001"
|
||||||
ServiceIDDoctors = "ZRYS00001"
|
ServiceIDDoctors = "ZRYS00001"
|
||||||
@@ -17,7 +19,9 @@ const (
|
|||||||
const codeRetryableTimeout = "405"
|
const codeRetryableTimeout = "405"
|
||||||
|
|
||||||
var servicePaths = map[string]string{
|
var servicePaths = map[string]string{
|
||||||
|
ServiceIDJKDACreate: "/jkda/create",
|
||||||
ServiceIDJKDAFind: "/auto/jkda/find",
|
ServiceIDJKDAFind: "/auto/jkda/find",
|
||||||
|
ServiceIDJKDAUpdate: "/jkda/update",
|
||||||
ServiceIDJKDAFindRqbj: "/jkda/findrqbj",
|
ServiceIDJKDAFindRqbj: "/jkda/findrqbj",
|
||||||
ServiceIDGridAddress: "/auto/wgdzcx/query",
|
ServiceIDGridAddress: "/auto/wgdzcx/query",
|
||||||
ServiceIDDoctors: "/auto/zryscx/query",
|
ServiceIDDoctors: "/auto/zryscx/query",
|
||||||
|
|||||||
+20
@@ -23,6 +23,19 @@ type PersonSignResult struct {
|
|||||||
PersonSign string `json:"personSign"`
|
PersonSign string `json:"personSign"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) CreateHealthRecord(ctx context.Context, req contract.HealthRecordCreate) (contract.HealthRecordSaveResult, Result, error) {
|
||||||
|
req.ManageInfo = c.manageInfo()
|
||||||
|
var out contract.HealthRecordSaveResult
|
||||||
|
result, err := c.callUploadInfo(ctx, ServiceIDJKDACreate, req, &out)
|
||||||
|
return out, result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) UpdateHealthRecord(ctx context.Context, req contract.HealthRecordCreate) (contract.HealthRecordSaveResult, Result, error) {
|
||||||
|
req.ManageInfo = c.manageInfo()
|
||||||
|
var out contract.HealthRecordSaveResult
|
||||||
|
result, err := c.callUploadInfo(ctx, ServiceIDJKDAUpdate, req, &out)
|
||||||
|
return out, result, err
|
||||||
|
}
|
||||||
func (c *Client) FindHealthRecord(ctx context.Context, query FindHealthRecordQuery) ([]contract.FindHealthRecord, Result, error) {
|
func (c *Client) FindHealthRecord(ctx context.Context, query FindHealthRecordQuery) ([]contract.FindHealthRecord, Result, error) {
|
||||||
baseInfo, err := query.baseInfo()
|
baseInfo, err := query.baseInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -43,6 +56,13 @@ func (c *Client) FindRqbj(ctx context.Context, query FindRqbjQuery) (PersonSignR
|
|||||||
return personSign, result, err
|
return personSign, result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) manageInfo() contract.ManageInfo {
|
||||||
|
return contract.ManageInfo{
|
||||||
|
DSFMC: c.config.UserName,
|
||||||
|
OperateUser: c.config.OperateUser,
|
||||||
|
OperateUnit: c.operateUnit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
func (q FindHealthRecordQuery) baseInfo() (map[string]string, error) {
|
func (q FindHealthRecordQuery) baseInfo() (map[string]string, error) {
|
||||||
fields := []queryField{
|
fields := []queryField{
|
||||||
{name: "idCard", value: q.IDCard},
|
{name: "idCard", value: q.IDCard},
|
||||||
|
|||||||
@@ -7,8 +7,83 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"chis_osi/contract"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestCreateHealthRecordCallsJKDA00001WithUploadInfo(t *testing.T) {
|
||||||
|
var seenPath string
|
||||||
|
var seenPayload map[string]any
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
seenPath = r.URL.Path
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&seenPayload); err != nil {
|
||||||
|
t.Fatalf("decode request: %v", err)
|
||||||
|
}
|
||||||
|
_, _ = w.Write([]byte(`{"code":"01","message":"操作成功","data":{"phrId":"PHR001","createUnit":"123456789"}}`))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client := newTestJKDAClient(t, server.URL)
|
||||||
|
resultData, result, err := client.CreateHealthRecord(context.Background(), contract.HealthRecordCreate{
|
||||||
|
BaseInfo: contract.HealthRecordBaseInfo{IDCard: "440000********1234", PersonName: "测试人员"},
|
||||||
|
HealthRecord: contract.HealthRecordCreateInfo{
|
||||||
|
CheckID: "CHK2026070700000001",
|
||||||
|
IDCard: "440000********1234",
|
||||||
|
PersonName: "测试人员",
|
||||||
|
RegionCode: "441625000000",
|
||||||
|
ManaDoctorID: "doc-001",
|
||||||
|
ManaUnitID: "123456789",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateHealthRecord: %v", err)
|
||||||
|
}
|
||||||
|
if !result.Success || result.Code != "01" {
|
||||||
|
t.Fatalf("result = %#v", result)
|
||||||
|
}
|
||||||
|
if seenPath != "/osi/api/jkda/create" {
|
||||||
|
t.Fatalf("path = %q", seenPath)
|
||||||
|
}
|
||||||
|
if seenPayload["serviceId"] != ServiceIDJKDACreate {
|
||||||
|
t.Fatalf("serviceId = %v", seenPayload["serviceId"])
|
||||||
|
}
|
||||||
|
uploadInfo := seenPayload["uploadinfo"].(map[string]any)
|
||||||
|
healthRecord := uploadInfo["healthRecord"].(map[string]any)
|
||||||
|
if healthRecord["checkId"] != "CHK2026070700000001" || healthRecord["manaUnitId"] != "123456789" {
|
||||||
|
t.Fatalf("healthRecord = %#v", healthRecord)
|
||||||
|
}
|
||||||
|
if resultData.PhrID != "PHR001" || resultData.CreateUnit != "123456789" {
|
||||||
|
t.Fatalf("resultData = %#v", resultData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateHealthRecordCallsJKDA00003(t *testing.T) {
|
||||||
|
var seenPath string
|
||||||
|
var seenPayload map[string]any
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
seenPath = r.URL.Path
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&seenPayload); err != nil {
|
||||||
|
t.Fatalf("decode request: %v", err)
|
||||||
|
}
|
||||||
|
_, _ = w.Write([]byte(`{"code":"01","message":"操作成功","data":{"phrId":"PHR001"}}`))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client := newTestJKDAClient(t, server.URL)
|
||||||
|
_, _, err := client.UpdateHealthRecord(context.Background(), contract.HealthRecordCreate{
|
||||||
|
BaseInfo: contract.HealthRecordBaseInfo{IDCard: "440000********1234"},
|
||||||
|
HealthRecord: contract.HealthRecordCreateInfo{CheckID: "CHK2026070700000001", PhrID: "PHR001"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("UpdateHealthRecord: %v", err)
|
||||||
|
}
|
||||||
|
if seenPath != "/osi/api/jkda/update" {
|
||||||
|
t.Fatalf("path = %q", seenPath)
|
||||||
|
}
|
||||||
|
if seenPayload["serviceId"] != ServiceIDJKDAUpdate {
|
||||||
|
t.Fatalf("serviceId = %v", seenPayload["serviceId"])
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestFindHealthRecordCallsJKDA00002WithBaseInfo(t *testing.T) {
|
func TestFindHealthRecordCallsJKDA00002WithBaseInfo(t *testing.T) {
|
||||||
var seenPath string
|
var seenPath string
|
||||||
var seenPayload map[string]any
|
var seenPayload map[string]any
|
||||||
|
|||||||
Reference in New Issue
Block a user