2026-07-07 14:36:40 +08:00
|
|
|
|
package mapping
|
|
|
|
|
|
|
2026-07-16 00:53:16 +08:00
|
|
|
|
import (
|
|
|
|
|
|
"strconv"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
"unicode/utf8"
|
|
|
|
|
|
|
|
|
|
|
|
"chis_osi/contract"
|
|
|
|
|
|
"chis_osi/source"
|
|
|
|
|
|
)
|
2026-07-07 23:11:17 +08:00
|
|
|
|
|
2026-07-07 14:36:40 +08:00
|
|
|
|
type PHISHealthRecord struct {
|
2026-07-16 00:53:16 +08:00
|
|
|
|
SourceSystem string
|
|
|
|
|
|
SourceRecordID string
|
|
|
|
|
|
UpdatedAt string
|
|
|
|
|
|
IDCard string
|
|
|
|
|
|
PersonName string
|
|
|
|
|
|
SexCode string
|
|
|
|
|
|
Birthday string
|
|
|
|
|
|
MobileNumber string
|
|
|
|
|
|
RegionCode string
|
|
|
|
|
|
RegionName string
|
|
|
|
|
|
ManaDoctorID string
|
|
|
|
|
|
ManaDoctorName string
|
|
|
|
|
|
ManaUnitID string
|
|
|
|
|
|
ManaUnitName string
|
|
|
|
|
|
NationCode string
|
|
|
|
|
|
EducationCode string
|
|
|
|
|
|
WorkCode string
|
|
|
|
|
|
MaritalStatusCode string
|
|
|
|
|
|
BloodTypeCode string
|
|
|
|
|
|
RhBloodCode string
|
|
|
|
|
|
InsuranceCode string
|
|
|
|
|
|
WorkPlace string
|
|
|
|
|
|
Contact string
|
|
|
|
|
|
ContactPhone string
|
|
|
|
|
|
RegisteredPermanent string
|
|
|
|
|
|
Address string
|
|
|
|
|
|
AddressNumber string
|
|
|
|
|
|
HomePlace string
|
|
|
|
|
|
HomePlaceNumber string
|
|
|
|
|
|
CardType string
|
|
|
|
|
|
IsFillSHHJ string
|
|
|
|
|
|
CreateDate string
|
|
|
|
|
|
DrugAllergy string
|
|
|
|
|
|
ExposureHistory string
|
|
|
|
|
|
FamilyFather string
|
|
|
|
|
|
FamilyMother string
|
|
|
|
|
|
FamilySiblings string
|
|
|
|
|
|
FamilyChildren string
|
|
|
|
|
|
GeneticDisease string
|
|
|
|
|
|
Disability string
|
|
|
|
|
|
PastDiseaseCode string
|
|
|
|
|
|
PastSurgeryCode string
|
|
|
|
|
|
PastTraumaCode string
|
|
|
|
|
|
PastTransfusionCode string
|
|
|
|
|
|
CookAirTool string
|
|
|
|
|
|
FuelType string
|
|
|
|
|
|
WaterSourceCode string
|
|
|
|
|
|
Washroom string
|
|
|
|
|
|
LivestockColumn string
|
2026-07-07 14:36:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type MappedHealthRecord struct {
|
2026-07-16 00:53:16 +08:00
|
|
|
|
CheckID string
|
|
|
|
|
|
IDCard string
|
|
|
|
|
|
PersonName string
|
|
|
|
|
|
SexCode string
|
|
|
|
|
|
Birthday string
|
|
|
|
|
|
MobileNumber string
|
|
|
|
|
|
RegionCode string
|
|
|
|
|
|
ManaDoctorID string
|
|
|
|
|
|
ManaUnitID string
|
|
|
|
|
|
NationCode string
|
|
|
|
|
|
EducationCode string
|
|
|
|
|
|
WorkCode string
|
|
|
|
|
|
MaritalStatusCode string
|
|
|
|
|
|
BloodTypeCode string
|
|
|
|
|
|
RhBloodCode string
|
|
|
|
|
|
InsuranceCode string
|
|
|
|
|
|
WorkPlace string
|
|
|
|
|
|
Contact string
|
|
|
|
|
|
ContactPhone string
|
|
|
|
|
|
RegisteredPermanent string
|
|
|
|
|
|
Address string
|
|
|
|
|
|
AddressNumber string
|
|
|
|
|
|
HomePlace string
|
|
|
|
|
|
HomePlaceNumber string
|
|
|
|
|
|
CardType string
|
|
|
|
|
|
IsFillSHHJ string
|
|
|
|
|
|
CreateDate string
|
|
|
|
|
|
PastHistory *contract.PastHistory
|
|
|
|
|
|
PastDiseases []contract.PastDisease
|
|
|
|
|
|
PastSurgeries []contract.PastSurgery
|
|
|
|
|
|
PastTraumas []contract.PastTrauma
|
|
|
|
|
|
PastTransfusions []contract.PastBloodTx
|
|
|
|
|
|
FamilyMiddle *contract.FamilyMiddle
|
2026-07-07 14:36:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type DictSnapshot struct {
|
|
|
|
|
|
Sex Dict
|
|
|
|
|
|
Nation Dict
|
|
|
|
|
|
Education Dict
|
|
|
|
|
|
Work Dict
|
|
|
|
|
|
MaritalStatus Dict
|
|
|
|
|
|
BloodType Dict
|
|
|
|
|
|
RhBlood Dict
|
|
|
|
|
|
Insurance Dict
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 21:16:52 +08:00
|
|
|
|
type MasterDataLookup interface {
|
|
|
|
|
|
RegionCodeByName(name string) (string, bool)
|
|
|
|
|
|
DoctorIDByName(name string) (string, bool)
|
|
|
|
|
|
ManaUnitIDByName(name string) (string, bool)
|
2026-07-16 00:53:16 +08:00
|
|
|
|
DoctorIDExists(id string) bool
|
|
|
|
|
|
ManaUnitIDExists(id string) bool
|
2026-07-07 21:16:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 14:36:40 +08:00
|
|
|
|
type MapContext struct {
|
2026-07-07 21:16:52 +08:00
|
|
|
|
Dicts DictSnapshot
|
|
|
|
|
|
MasterData MasterDataLookup
|
2026-07-07 14:36:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func DefaultDictSnapshot() DictSnapshot {
|
|
|
|
|
|
return DictSnapshot{
|
|
|
|
|
|
Sex: SexDict,
|
|
|
|
|
|
Nation: NationDict,
|
|
|
|
|
|
Education: EducationDict,
|
|
|
|
|
|
Work: WorkDict,
|
|
|
|
|
|
MaritalStatus: MaritalStatusDict,
|
|
|
|
|
|
BloodType: BloodTypeDict,
|
|
|
|
|
|
RhBlood: RhBloodDict,
|
|
|
|
|
|
Insurance: InsuranceDict,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func MapHealthRecord(src PHISHealthRecord, ctx MapContext) (MappedHealthRecord, []ValidationError) {
|
|
|
|
|
|
if ctx.Dicts.Sex.Name == "" {
|
|
|
|
|
|
ctx.Dicts = DefaultDictSnapshot()
|
|
|
|
|
|
}
|
2026-07-07 21:16:52 +08:00
|
|
|
|
resolveMasterData(&src, ctx.MasterData)
|
2026-07-07 14:36:40 +08:00
|
|
|
|
var errs []ValidationError
|
|
|
|
|
|
for _, field := range []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
value string
|
|
|
|
|
|
}{
|
|
|
|
|
|
{name: "idCard", value: src.IDCard},
|
|
|
|
|
|
{name: "personName", value: src.PersonName},
|
|
|
|
|
|
{name: "sexCode", value: src.SexCode},
|
|
|
|
|
|
{name: "birthday", value: src.Birthday},
|
|
|
|
|
|
{name: "mobileNumber", value: src.MobileNumber},
|
|
|
|
|
|
{name: "regionCode", value: src.RegionCode},
|
|
|
|
|
|
{name: "manaDoctorId", value: src.ManaDoctorID},
|
|
|
|
|
|
{name: "manaUnitId", value: src.ManaUnitID},
|
|
|
|
|
|
} {
|
|
|
|
|
|
if field.value == "" {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: field.name, Reason: "required"})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mapped := MappedHealthRecord{
|
2026-07-16 00:53:16 +08:00
|
|
|
|
CheckID: GenerateCheckID(src.SourceSystem, "JKDA", src.SourceRecordID),
|
|
|
|
|
|
IDCard: src.IDCard,
|
|
|
|
|
|
PersonName: src.PersonName,
|
|
|
|
|
|
Birthday: src.Birthday,
|
|
|
|
|
|
MobileNumber: src.MobileNumber,
|
|
|
|
|
|
RegionCode: src.RegionCode,
|
|
|
|
|
|
ManaDoctorID: src.ManaDoctorID,
|
|
|
|
|
|
ManaUnitID: src.ManaUnitID,
|
|
|
|
|
|
WorkPlace: src.WorkPlace,
|
|
|
|
|
|
Contact: src.Contact,
|
|
|
|
|
|
ContactPhone: src.ContactPhone,
|
|
|
|
|
|
RegisteredPermanent: src.RegisteredPermanent,
|
|
|
|
|
|
Address: src.Address,
|
|
|
|
|
|
AddressNumber: src.AddressNumber,
|
|
|
|
|
|
HomePlace: src.HomePlace,
|
|
|
|
|
|
HomePlaceNumber: src.HomePlaceNumber,
|
|
|
|
|
|
CardType: src.CardType,
|
|
|
|
|
|
IsFillSHHJ: strings.ToLower(strings.TrimSpace(src.IsFillSHHJ)),
|
|
|
|
|
|
CreateDate: src.CreateDate,
|
2026-07-07 14:36:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
mapped.SexCode = mapCode(ctx.Dicts.Sex, src.SexCode, &errs)
|
|
|
|
|
|
mapped.NationCode = mapCode(ctx.Dicts.Nation, src.NationCode, &errs)
|
|
|
|
|
|
mapped.EducationCode = mapCode(ctx.Dicts.Education, src.EducationCode, &errs)
|
|
|
|
|
|
mapped.WorkCode = mapCode(ctx.Dicts.Work, src.WorkCode, &errs)
|
|
|
|
|
|
mapped.MaritalStatusCode = mapCode(ctx.Dicts.MaritalStatus, src.MaritalStatusCode, &errs)
|
|
|
|
|
|
mapped.BloodTypeCode = mapCode(ctx.Dicts.BloodType, src.BloodTypeCode, &errs)
|
|
|
|
|
|
mapped.RhBloodCode = mapCode(ctx.Dicts.RhBlood, src.RhBloodCode, &errs)
|
2026-07-16 00:53:16 +08:00
|
|
|
|
mapped.InsuranceCode = mapDelimitedCode(ctx.Dicts.Insurance, src.InsuranceCode, &errs)
|
2026-07-07 14:36:40 +08:00
|
|
|
|
return mapped, errs
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 00:53:16 +08:00
|
|
|
|
func MapHealthRecordTask(task source.HealthRecordTask, ctx MapContext) (contract.HealthRecordCreate, []ValidationError) {
|
|
|
|
|
|
r := task.Record
|
|
|
|
|
|
src := PHISHealthRecord{
|
|
|
|
|
|
SourceSystem: "PHIS", SourceRecordID: task.SourceRecordID(), UpdatedAt: r.UpdatedAt,
|
|
|
|
|
|
IDCard: r.IDCard, PersonName: r.PersonName, SexCode: r.SexCode, Birthday: r.Birthday,
|
|
|
|
|
|
MobileNumber: r.MobileNumber, RegionCode: r.RegionCode, ManaDoctorID: r.ManaDoctorID,
|
|
|
|
|
|
ManaUnitID: r.ManaUnitID, NationCode: r.NationCode, EducationCode: r.EducationCode,
|
|
|
|
|
|
WorkCode: r.WorkCode, MaritalStatusCode: r.MaritalStatusCode, BloodTypeCode: r.BloodTypeCode,
|
|
|
|
|
|
RhBloodCode: r.RhBloodCode, InsuranceCode: r.InsuranceCode, WorkPlace: r.WorkPlace,
|
|
|
|
|
|
Contact: r.Contact, ContactPhone: r.ContactPhone, RegisteredPermanent: r.RegisteredPermanent,
|
|
|
|
|
|
Address: r.Address, AddressNumber: r.AddressNumber, HomePlace: r.HomePlace,
|
|
|
|
|
|
HomePlaceNumber: r.HomePlaceNumber, CardType: firstNonEmpty(r.CardType, task.CardType),
|
|
|
|
|
|
IsFillSHHJ: r.IsFillSHHJ, DrugAllergy: r.DrugAllergy, ExposureHistory: r.ExposureHistory,
|
|
|
|
|
|
FamilyFather: r.FamilyFather, FamilyMother: r.FamilyMother, FamilySiblings: r.FamilySiblings,
|
|
|
|
|
|
FamilyChildren: r.FamilyChildren, GeneticDisease: r.GeneticDisease, Disability: r.Disability,
|
|
|
|
|
|
PastDiseaseCode: r.PastDiseaseCode, PastSurgeryCode: r.PastSurgeryCode,
|
|
|
|
|
|
PastTraumaCode: r.PastTraumaCode, PastTransfusionCode: r.PastTransfusionCode,
|
|
|
|
|
|
CookAirTool: r.CookAirTool, FuelType: r.FuelType, WaterSourceCode: r.WaterSourceCode,
|
|
|
|
|
|
Washroom: r.Washroom, LivestockColumn: r.LivestockColumn,
|
|
|
|
|
|
CreateDate: r.CreateDate,
|
|
|
|
|
|
}
|
|
|
|
|
|
mapped, errs := MapHealthRecord(src, ctx)
|
|
|
|
|
|
errs = append(errs, validateTaskFields(task)...)
|
|
|
|
|
|
errs = append(errs, validateMasterData(src, ctx.MasterData)...)
|
|
|
|
|
|
|
|
|
|
|
|
pastHistory := &contract.PastHistory{}
|
|
|
|
|
|
pastHistory.YWGMS = validateDelimitedCodes("ywgms", src.DrugAllergy, allergyCodes, &errs)
|
|
|
|
|
|
pastHistory.BLS = validateDelimitedCodes("bls", src.ExposureHistory, exposureCodes, &errs)
|
|
|
|
|
|
pastHistory.JZSFQN = validateDelimitedCodes("jzsfqn", src.FamilyFather, fatherCodes, &errs)
|
|
|
|
|
|
pastHistory.JZSMQ = validateDelimitedCodes("jzsmq", src.FamilyMother, motherCodes, &errs)
|
|
|
|
|
|
pastHistory.JZSXDJM = validateDelimitedCodes("jzsxdjm", src.FamilySiblings, siblingCodes, &errs)
|
|
|
|
|
|
pastHistory.JZSZN = validateDelimitedCodes("jzszn", src.FamilyChildren, childrenCodes, &errs)
|
|
|
|
|
|
pastHistory.YCBS = validateDelimitedCodes("ycbs", src.GeneticDisease, geneticCodes, &errs)
|
|
|
|
|
|
pastHistory.CJQK = validateDelimitedCodes("cjqk", src.Disability, disabilityCodes, &errs)
|
|
|
|
|
|
if *pastHistory != (contract.PastHistory{}) {
|
|
|
|
|
|
mapped.PastHistory = pastHistory
|
|
|
|
|
|
}
|
|
|
|
|
|
mapped.PastDiseases = mapPastDiseases(validateDelimitedCodes("jwsjbcode", src.PastDiseaseCode, diseaseCodes, &errs))
|
|
|
|
|
|
mapped.PastSurgeries = mapPastSurgeries(validateDelimitedCodes("jwssscode", src.PastSurgeryCode, surgeryCodes, &errs))
|
|
|
|
|
|
mapped.PastTraumas = mapPastTraumas(validateDelimitedCodes("jwswscode", src.PastTraumaCode, traumaCodes, &errs))
|
|
|
|
|
|
mapped.PastTransfusions = mapPastTransfusions(validateDelimitedCodes("jwssxcode", src.PastTransfusionCode, transfusionCodes, &errs))
|
|
|
|
|
|
|
|
|
|
|
|
if mapped.IsFillSHHJ == "y" {
|
|
|
|
|
|
mapped.FamilyMiddle = &contract.FamilyMiddle{
|
|
|
|
|
|
CookAirTool: validateSingleCode("cookAirTool", src.CookAirTool, cookAirCodes, &errs),
|
|
|
|
|
|
FuelType: validateSingleCode("fuelType", src.FuelType, fuelCodes, &errs),
|
|
|
|
|
|
WaterSourceCode: validateSingleCode("waterSourceCode", src.WaterSourceCode, waterCodes, &errs),
|
|
|
|
|
|
Washroom: validateSingleCode("washroom", src.Washroom, washroomCodes, &errs),
|
|
|
|
|
|
LivestockColumn: validateSingleCode("livestockColumn", src.LivestockColumn, livestockCodes, &errs),
|
|
|
|
|
|
IsFillSHHJ: "y",
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return BuildHealthRecordCreate(mapped), errs
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 02:00:56 +08:00
|
|
|
|
// ValidateHealthRecordTask performs all local mapping validation without
|
|
|
|
|
|
// claiming that the supplied doctor or organization exists in CHIS.
|
|
|
|
|
|
func ValidateHealthRecordTask(task source.HealthRecordTask) []ValidationError {
|
|
|
|
|
|
_, errs := MapHealthRecordTask(task, MapContext{
|
|
|
|
|
|
Dicts: DefaultDictSnapshot(),
|
|
|
|
|
|
MasterData: submittedHealthRecordMasterData{
|
|
|
|
|
|
doctorID: task.Record.ManaDoctorID,
|
|
|
|
|
|
unitID: task.Record.ManaUnitID,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
return errs
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type submittedHealthRecordMasterData struct {
|
|
|
|
|
|
doctorID string
|
|
|
|
|
|
unitID string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (m submittedHealthRecordMasterData) RegionCodeByName(string) (string, bool) { return "", false }
|
|
|
|
|
|
func (m submittedHealthRecordMasterData) DoctorIDByName(string) (string, bool) { return "", false }
|
|
|
|
|
|
func (m submittedHealthRecordMasterData) ManaUnitIDByName(string) (string, bool) { return "", false }
|
|
|
|
|
|
func (m submittedHealthRecordMasterData) DoctorIDExists(id string) bool {
|
|
|
|
|
|
return strings.TrimSpace(id) != "" && strings.TrimSpace(id) == strings.TrimSpace(m.doctorID)
|
|
|
|
|
|
}
|
|
|
|
|
|
func (m submittedHealthRecordMasterData) ManaUnitIDExists(id string) bool {
|
|
|
|
|
|
return strings.TrimSpace(id) != "" && strings.TrimSpace(id) == strings.TrimSpace(m.unitID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 00:53:16 +08:00
|
|
|
|
func validateMasterData(src PHISHealthRecord, lookup MasterDataLookup) []ValidationError {
|
|
|
|
|
|
if lookup == nil {
|
|
|
|
|
|
return []ValidationError{{Field: "masterData", Reason: "CHIS dictionary snapshot is required"}}
|
|
|
|
|
|
}
|
|
|
|
|
|
var errs []ValidationError
|
|
|
|
|
|
if src.ManaDoctorID != "" && !lookup.DoctorIDExists(src.ManaDoctorID) {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: "manaDoctorId", Code: src.ManaDoctorID, Reason: "not found in CHIS doctor dictionary"})
|
|
|
|
|
|
}
|
|
|
|
|
|
if src.ManaUnitID != "" && !lookup.ManaUnitIDExists(src.ManaUnitID) {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: "manaUnitId", Code: src.ManaUnitID, Reason: "not found in CHIS organization dictionary"})
|
|
|
|
|
|
}
|
|
|
|
|
|
return errs
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 21:16:52 +08:00
|
|
|
|
func resolveMasterData(src *PHISHealthRecord, lookup MasterDataLookup) {
|
|
|
|
|
|
if lookup == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if src.RegionCode == "" && src.RegionName != "" {
|
|
|
|
|
|
if code, ok := lookup.RegionCodeByName(src.RegionName); ok {
|
|
|
|
|
|
src.RegionCode = code
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if src.ManaDoctorID == "" && src.ManaDoctorName != "" {
|
|
|
|
|
|
if code, ok := lookup.DoctorIDByName(src.ManaDoctorName); ok {
|
|
|
|
|
|
src.ManaDoctorID = code
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if src.ManaUnitID == "" && src.ManaUnitName != "" {
|
|
|
|
|
|
if code, ok := lookup.ManaUnitIDByName(src.ManaUnitName); ok {
|
|
|
|
|
|
src.ManaUnitID = code
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-07 14:36:40 +08:00
|
|
|
|
func mapCode(dict Dict, code string, errs *[]ValidationError) string {
|
|
|
|
|
|
if code == "" {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
mapped, err := dict.PHISToOSI(code)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if validationErr, ok := err.(ValidationError); ok {
|
|
|
|
|
|
*errs = append(*errs, validationErr)
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
*errs = append(*errs, ValidationError{Field: dict.Field, Code: code, Reason: err.Error()})
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
return mapped
|
|
|
|
|
|
}
|
2026-07-16 00:53:16 +08:00
|
|
|
|
|
|
|
|
|
|
func mapDelimitedCode(dict Dict, value string, errs *[]ValidationError) string {
|
|
|
|
|
|
parts := splitCodes(value)
|
|
|
|
|
|
mapped := make([]string, 0, len(parts))
|
|
|
|
|
|
for _, code := range parts {
|
|
|
|
|
|
mappedCode := mapCode(dict, code, errs)
|
|
|
|
|
|
if mappedCode != "" {
|
|
|
|
|
|
mapped = append(mapped, mappedCode)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return strings.Join(mapped, ",")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 23:11:17 +08:00
|
|
|
|
func BuildHealthRecordCreate(mapped MappedHealthRecord) contract.HealthRecordCreate {
|
|
|
|
|
|
return contract.HealthRecordCreate{
|
|
|
|
|
|
BaseInfo: contract.HealthRecordBaseInfo{
|
|
|
|
|
|
IDCard: mapped.IDCard,
|
|
|
|
|
|
PersonName: mapped.PersonName,
|
|
|
|
|
|
},
|
2026-07-16 00:53:16 +08:00
|
|
|
|
ManageInfo: contract.ManageInfo{OperateUser: mapped.ManaDoctorID},
|
2026-07-07 23:11:17 +08:00
|
|
|
|
HealthRecord: contract.HealthRecordCreateInfo{
|
2026-07-16 00:53:16 +08:00
|
|
|
|
CheckID: mapped.CheckID,
|
|
|
|
|
|
IDCard: mapped.IDCard,
|
|
|
|
|
|
PersonName: mapped.PersonName,
|
|
|
|
|
|
SexCode: mapped.SexCode,
|
|
|
|
|
|
Birthday: mapped.Birthday,
|
|
|
|
|
|
MobileNumber: mapped.MobileNumber,
|
|
|
|
|
|
WorkPlace: mapped.WorkPlace,
|
|
|
|
|
|
Contact: mapped.Contact,
|
|
|
|
|
|
ContactPhone: mapped.ContactPhone,
|
|
|
|
|
|
RegisteredPermanent: mapped.RegisteredPermanent,
|
|
|
|
|
|
RegionCode: mapped.RegionCode,
|
|
|
|
|
|
Address: mapped.Address,
|
|
|
|
|
|
AddressNumber: mapped.AddressNumber,
|
|
|
|
|
|
HomePlace: mapped.HomePlace,
|
|
|
|
|
|
HomePlaceNumber: mapped.HomePlaceNumber,
|
|
|
|
|
|
CardType: mapped.CardType,
|
|
|
|
|
|
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,
|
|
|
|
|
|
IsFillSHHJ: mapped.IsFillSHHJ,
|
|
|
|
|
|
CreateDate: mapped.CreateDate,
|
2026-07-07 23:11:17 +08:00
|
|
|
|
},
|
2026-07-16 00:53:16 +08:00
|
|
|
|
PastHistory: mapped.PastHistory,
|
|
|
|
|
|
PastDiseases: mapped.PastDiseases,
|
|
|
|
|
|
PastSurgeries: mapped.PastSurgeries,
|
|
|
|
|
|
PastTraumas: mapped.PastTraumas,
|
|
|
|
|
|
PastTransfusions: mapped.PastTransfusions,
|
|
|
|
|
|
FamilyMiddle: mapped.FamilyMiddle,
|
2026-07-07 23:11:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-16 00:53:16 +08:00
|
|
|
|
|
|
|
|
|
|
func validateTaskFields(task source.HealthRecordTask) []ValidationError {
|
|
|
|
|
|
r := task.Record
|
|
|
|
|
|
var errs []ValidationError
|
|
|
|
|
|
for _, field := range []struct{ name, value string }{
|
|
|
|
|
|
{"archId", task.ArchID}, {"contact", r.Contact}, {"contactPhone", r.ContactPhone},
|
|
|
|
|
|
{"registeredPermanent", r.RegisteredPermanent}, {"cardType", firstNonEmpty(r.CardType, task.CardType)},
|
|
|
|
|
|
{"nationCode", r.NationCode}, {"bloodTypeCode", r.BloodTypeCode}, {"rhBloodCode", r.RhBloodCode},
|
|
|
|
|
|
{"educationCode", r.EducationCode}, {"workCode", r.WorkCode}, {"maritalStatusCode", r.MaritalStatusCode},
|
|
|
|
|
|
{"insuranceCode", r.InsuranceCode}, {"ywgms", r.DrugAllergy}, {"cjqk", r.Disability},
|
|
|
|
|
|
} {
|
|
|
|
|
|
if strings.TrimSpace(field.value) == "" {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: field.name, Reason: "required"})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, field := range []struct {
|
|
|
|
|
|
name, value string
|
|
|
|
|
|
max int
|
|
|
|
|
|
}{
|
|
|
|
|
|
{"idCard", r.IDCard, 18}, {"personName", r.PersonName, 50}, {"workPlace", r.WorkPlace, 70},
|
|
|
|
|
|
{"mobileNumber", r.MobileNumber, 20}, {"contact", r.Contact, 50}, {"contactPhone", r.ContactPhone, 20},
|
|
|
|
|
|
{"address", r.Address, 100}, {"addressNumber", r.AddressNumber, 70}, {"homePlace", r.HomePlace, 100},
|
|
|
|
|
|
{"homePlaceNumber", r.HomePlaceNumber, 70}, {"manaDoctorId", r.ManaDoctorID, 20}, {"manaUnitId", r.ManaUnitID, 20},
|
|
|
|
|
|
} {
|
|
|
|
|
|
if utf8.RuneCountInString(field.value) > field.max {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: field.name, Reason: "max length " + strconv.Itoa(field.max)})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, field := range []struct {
|
|
|
|
|
|
name, value string
|
|
|
|
|
|
length int
|
|
|
|
|
|
}{
|
|
|
|
|
|
{"idCard", r.IDCard, 18},
|
|
|
|
|
|
{"regionCode", r.RegionCode, 12},
|
|
|
|
|
|
} {
|
|
|
|
|
|
if utf8.RuneCountInString(field.value) != field.length {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: field.name, Reason: "length " + strconv.Itoa(field.length)})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if _, err := time.Parse("2006-01-02", r.Birthday); err != nil {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: "birthday", Reason: "invalid date, want yyyy-MM-dd"})
|
|
|
|
|
|
}
|
|
|
|
|
|
if r.RegisteredPermanent != "1" && r.RegisteredPermanent != "2" {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: "registeredPermanent", Code: r.RegisteredPermanent, Reason: "unknown code"})
|
|
|
|
|
|
}
|
|
|
|
|
|
fill := strings.ToLower(strings.TrimSpace(r.IsFillSHHJ))
|
|
|
|
|
|
if fill != "y" && fill != "n" {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: "isFillShhj", Code: r.IsFillSHHJ, Reason: "unknown code"})
|
|
|
|
|
|
}
|
|
|
|
|
|
if task.Doctor.DoctorID != "" && task.Doctor.DoctorID != r.ManaDoctorID {
|
|
|
|
|
|
errs = append(errs, ValidationError{Field: "manaDoctorId", Reason: "does not match data.doctor.doctorId"})
|
|
|
|
|
|
}
|
|
|
|
|
|
return errs
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func firstNonEmpty(values ...string) string {
|
|
|
|
|
|
for _, value := range values {
|
|
|
|
|
|
if strings.TrimSpace(value) != "" {
|
|
|
|
|
|
return strings.TrimSpace(value)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func splitCodes(value string) []string {
|
|
|
|
|
|
replacer := strings.NewReplacer(",", ",", "、", ",", ";", ",", ";", ",")
|
|
|
|
|
|
var out []string
|
|
|
|
|
|
for _, part := range strings.Split(replacer.Replace(value), ",") {
|
|
|
|
|
|
if code := strings.TrimSpace(part); code != "" {
|
|
|
|
|
|
out = append(out, code)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return out
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func validateDelimitedCodes(field, value string, allowed map[string]struct{}, errs *[]ValidationError) string {
|
|
|
|
|
|
parts := splitCodes(value)
|
|
|
|
|
|
valid := make([]string, 0, len(parts))
|
|
|
|
|
|
for _, code := range parts {
|
|
|
|
|
|
if _, ok := allowed[code]; !ok {
|
|
|
|
|
|
*errs = append(*errs, ValidationError{Field: field, Code: code, Reason: "unknown code"})
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
valid = append(valid, code)
|
|
|
|
|
|
}
|
|
|
|
|
|
return strings.Join(valid, ",")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func validateSingleCode(field, value string, allowed map[string]struct{}, errs *[]ValidationError) string {
|
|
|
|
|
|
if parts := splitCodes(value); len(parts) != 1 {
|
|
|
|
|
|
*errs = append(*errs, ValidationError{Field: field, Reason: "must contain exactly one code"})
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
return validateDelimitedCodes(field, value, allowed, errs)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func codeSet(values ...string) map[string]struct{} {
|
|
|
|
|
|
set := make(map[string]struct{}, len(values))
|
|
|
|
|
|
for _, value := range values {
|
|
|
|
|
|
set[value] = struct{}{}
|
|
|
|
|
|
}
|
|
|
|
|
|
return set
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
|
allergyCodes = codeSet("0101", "0102", "0103", "0104", "0109")
|
|
|
|
|
|
exposureCodes = codeSet("1201", "1202", "1203", "1204")
|
|
|
|
|
|
fatherCodes = numberedCodes("07")
|
|
|
|
|
|
motherCodes = numberedCodes("08")
|
|
|
|
|
|
siblingCodes = numberedCodes("09")
|
|
|
|
|
|
childrenCodes = numberedCodes("10")
|
|
|
|
|
|
geneticCodes = codeSet("0501", "0502")
|
|
|
|
|
|
disabilityCodes = codeSet("1101", "1102", "1103", "1104", "1105", "1106", "1107", "1108", "1109", "1199")
|
|
|
|
|
|
diseaseCodes = codeSet("0201", "0202", "0203", "0204", "0205", "0206", "0207", "0208", "0209", "0210", "0211", "0212", "0213", "0214", "0215", "0298", "0299")
|
|
|
|
|
|
surgeryCodes = codeSet("0301", "0302")
|
|
|
|
|
|
traumaCodes = codeSet("0601", "0602")
|
|
|
|
|
|
transfusionCodes = codeSet("0401", "0402")
|
|
|
|
|
|
cookAirCodes = codeSet("1", "2", "3", "4", "9")
|
|
|
|
|
|
fuelCodes = codeSet("1", "2", "3", "4", "5", "9")
|
|
|
|
|
|
waterCodes = codeSet("1", "2", "3", "4", "5", "9")
|
|
|
|
|
|
washroomCodes = codeSet("1", "2", "3", "4", "5", "6")
|
|
|
|
|
|
livestockCodes = codeSet("0", "1", "2", "3")
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func numberedCodes(prefix string) map[string]struct{} {
|
|
|
|
|
|
values := []string{prefix + "01", prefix + "02", prefix + "03", prefix + "04", prefix + "05", prefix + "06", prefix + "07", prefix + "08", prefix + "09", prefix + "10", prefix + "11", prefix + "99"}
|
|
|
|
|
|
return codeSet(values...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func mapPastDiseases(value string) []contract.PastDisease {
|
|
|
|
|
|
parts := splitCodes(value)
|
|
|
|
|
|
out := make([]contract.PastDisease, 0, len(parts))
|
|
|
|
|
|
for _, code := range parts {
|
|
|
|
|
|
out = append(out, contract.PastDisease{Code: code})
|
|
|
|
|
|
}
|
|
|
|
|
|
return out
|
|
|
|
|
|
}
|
|
|
|
|
|
func mapPastSurgeries(value string) []contract.PastSurgery {
|
|
|
|
|
|
parts := splitCodes(value)
|
|
|
|
|
|
out := make([]contract.PastSurgery, 0, len(parts))
|
|
|
|
|
|
for _, code := range parts {
|
|
|
|
|
|
out = append(out, contract.PastSurgery{Code: code})
|
|
|
|
|
|
}
|
|
|
|
|
|
return out
|
|
|
|
|
|
}
|
|
|
|
|
|
func mapPastTraumas(value string) []contract.PastTrauma {
|
|
|
|
|
|
parts := splitCodes(value)
|
|
|
|
|
|
out := make([]contract.PastTrauma, 0, len(parts))
|
|
|
|
|
|
for _, code := range parts {
|
|
|
|
|
|
out = append(out, contract.PastTrauma{Code: code})
|
|
|
|
|
|
}
|
|
|
|
|
|
return out
|
|
|
|
|
|
}
|
|
|
|
|
|
func mapPastTransfusions(value string) []contract.PastBloodTx {
|
|
|
|
|
|
parts := splitCodes(value)
|
|
|
|
|
|
out := make([]contract.PastBloodTx, 0, len(parts))
|
|
|
|
|
|
for _, code := range parts {
|
|
|
|
|
|
out = append(out, contract.PastBloodTx{Code: code})
|
|
|
|
|
|
}
|
|
|
|
|
|
return out
|
|
|
|
|
|
}
|