package management import ( "fmt" "nCovTrack-Backend/global" "nCovTrack-Backend/models" "nCovTrack-Backend/service/notify" "nCovTrack-Backend/utils" "time" ) //PullFromFaker Pull observations from faker system func PullFromFaker(claims models.TokenClaims, patientId int) int64 { // Get patient's identification by id queryMap := []map[string]interface{}{{"id": patientId}} patient := models.GetField[models.BackObservation](queryMap, false, "identification") if patient == nil || patient["identification"] == nil { return -1 } // Pull contacts form faker observations := queryContacts(patient["identification"].(string)) // Set ContactPerson for i := range observations { observations[i].ContactPerson = patientId } // Insert into db var observationsJMap []map[string]interface{} utils.Strcut2Map(observations, &observationsJMap) observationsCMap := models.MapsJ2c[models.BackObservation](observationsJMap, false) models.BeforeBatchSave(&observationsCMap, claims.ID) _, rowsAffected := models.BatchInsert[models.BackObservation](observationsCMap) // Generate situation record and send notify to the region if rowsAffected != 0 { pullSituationRecord(claims, observations) pullNotify(observations) } return rowsAffected } //ListObservation list observations with query func ListObservation(jsonMap map[string]interface{}) []models.ListObservation { colMap := models.MapJ2c[models.BackObservation](jsonMap, true) queryMap := []map[string]interface{}{colMap} return listObservation(queryMap) } //GetObservation get observation by id func GetObservation(id int) map[string]interface{} { // Obseration observationCMap := models.GetField[models.BackObservation]([]map[string]interface{}{{"id": id}}, true, "is_delete") observationJMap := models.MapC2j[models.BackObservation](observationCMap, true) // Pcr record pcrRecordCMap := models.ListField[models.BackPcr]([]map[string]interface{}{{"observation": id}}, true, "is_delete") pcrRecordJMap := models.MapsC2j[models.BackPcr](*pcrRecordCMap, true) // Situation record situationRecordCMap := models.ListField[models.BackSituationRecord]([]map[string]interface{}{{"observation": id}}, true, "is_delete") situationRecordJMap := models.MapsC2j[models.BackSituationRecord](*situationRecordCMap, true) // Assemble observationJMap["pcrRecord"] = pcrRecordJMap observationJMap["situationRecord"] = situationRecordJMap return observationJMap } //InsertObservation add observation func InsertObservation(claims models.TokenClaims, jsonMap map[string]interface{}) bool { colMap := models.MapJ2c[models.BackObservation](jsonMap, true) models.BeforeSave(colMap, claims.ID) ok, rowsAffected := models.Upsert[models.BackObservation](colMap) queryMap := []map[string]interface{}{{"identification": jsonMap["identification"]}} // Get result after insert, assemble record will use it. newObservation := models.GetField[models.BackObservation](queryMap, true) recordOk := insertSituationRecord(claims, map[string]interface{}{}, newObservation) return (ok && recordOk && rowsAffected != 0) } func UpdateObservation(claims models.TokenClaims, jsonMap map[string]any) bool { // Pre actions colMap := models.MapJ2c[models.BackObservation](jsonMap, true) models.BeforeSave(colMap, claims.ID) queryMap := []map[string]interface{}{{"id": jsonMap["id"]}} // Get old record oldMap := models.Get[models.BackObservation](queryMap) if oldMap == nil || len(oldMap) == 0 { return false } // Deal with region update if colMap["region"] != nil && oldMap["region"].(string) != colMap["region"].(string) { updateObservationRegion(colMap["region"].(string)) } // Deal with situation update if colMap["health_situation"] != nil && int(colMap["health_situation"].(float64)) != oldMap["health_situation"].(int) || colMap["measure_situation"] != nil && int(colMap["measure_situation"].(float64)) != oldMap["measure_situation"].(int) { updateSituation(claims, oldMap, colMap) } ok, rowsAffected := models.Update[models.BackObservation](queryMap, colMap) return ok && rowsAffected != 0 } //TreeifyObservations get the transmission chains of observations // Param direction: up, down, all func TreeifyObservations(observationId int, direction string) *models.TreeObservation { // Get arrays form back observation var observations []models.BackObservation if direction != "down" { observations = append(observations, treeifyObservations(observationId, true)...) } if direction != "up" { observations = append(observations, treeifyObservations(observationId, false)...) } // Treeify observationMap := map[int]*models.TreeObservation{} for _, observation := range observations { observationMap[observation.ID] = &models.TreeObservation{ ID: observation.ID, Name: observation.Name, Age: observation.Age, Sex: observation.Sex, Phone: observation.Phone, Identification: observation.Identification, ContactPerson: observation.ContactPerson, Region: observation.Region, Address: observation.Address, HealthSituation: observation.HealthSituation, HealthChangeTime: observation.HealthChangeTime, CreateUser: observation.CreateUser, CreateTime: observation.CreateTime, ModifyUser: observation.ModifyUser, ModifyTime: observation.ModifyTime, } } var rootKey int for k := range observationMap { parentId := observationMap[k].ContactPerson parent := observationMap[parentId] if parent == nil { rootKey = k continue } parent.Children = append(parent.Children, observationMap[k]) } return observationMap[rootKey] } //treeifyObservations query tree data from db // Param direction up: true, down: false func treeifyObservations(observationId int, direction bool) []models.BackObservation { var res []models.BackObservation var directionCond string if direction { directionCond = "p.id = cte.contact_person" } else { directionCond = "cte.id = p.contact_person" } querySql := fmt.Sprintf(` WITH RECURSIVE cte (id, name, age, sex, phone, identification, contact_person, region, address, health_situation, health_change_time, trajectory, create_user, modify_user, create_time, modify_time) AS ( SELECT id, name, age, sex, phone, identification, contact_person, region, address, health_situation, health_change_time, trajectory, create_user, modify_user, create_time, modify_time FROM back_observation WHERE contact_person = ? UNION ALL SELECT p.id, p.name, p.age, p.sex, p.phone, p.identification, p.contact_person, p.region, p.address, p.health_situation, p.health_change_time, p.trajectory, p.create_user, p.modify_user, p.create_time, p.modify_time FROM back_observation p INNER JOIN cte ON %s ) SELECT * FROM cte `, directionCond) global.Db.Model(models.BackObservation{}).Raw(querySql, observationId).Scan(&res) return res } //listObservation the queryMap need with table name // Param queryMap the colMap of models.BackObservation func listObservation(queryMap []map[string]interface{}) []models.ListObservation { var observations []models.ListObservation tx := global.Db.Model(new((models.BackObservation))). Select("back_observation.*, back_pcr.detect_time AS pcr_time, detect_result AS pcr_result," + "back_situation_record.create_time AS record_time, back_situation_record.record AS record"). Joins("LEFT JOIN back_pcr ON back_observation.id = back_pcr.observation"). Joins("LEFT JOIN back_situation_record ON back_observation.id = back_situation_record.observation") for _, query := range queryMap { query["is_delete"] = 0 // Add the table prefix fullQuery := map[string]interface{}{} for k := range query { fullQuery["back_observation."+k] = query[k] } tx = tx.Or(fullQuery) } tx.Find(&observations) return observations } //pullSituationRecord push situation record after pull observations func pullSituationRecord(claims models.TokenClaims, observations []models.BackObservation) { // Query observations' id var identifications []string for _, observation := range observations { identifications = append(identifications, observation.Identification) } queryMap := []map[string]interface{}{{"identification": identifications}} observationMaps := models.ListField[models.BackObservation](queryMap, false, "id", "identification") // Assemble records var records []models.BackSituationRecord recordContent := record___20() for _, observationMap := range *observationMaps { record := models.BackSituationRecord{Observation: observationMap["id"].(int), Record: recordContent} records = append(records, record) } // Insert into db var recordsJMaps []map[string]interface{} utils.Strcut2Map(records, &recordsJMaps) recordsCMaps := models.MapsJ2c[models.BackSituationRecord](recordsJMaps, true) models.BeforeBatchSave(&recordsCMaps, claims.ID) models.BatchInsert[models.BackSituationRecord](recordsCMaps) } //pullNotify send notify after pull observations func pullNotify(observations []models.BackObservation) { var regions []string for _, observation := range observations { regions = append(regions, observation.Region) } regions = utils.Distinct(regions) notification := models.BackNotification{Time: time.Now(), Kind: "疫情", Content: "出现了新的接触者,请及时登录系统进行处理"} sendInfo := models.SendInfo{Region: regions, Channel: []int{0, 1}, Notification: notification} notify.SendNotify(sendInfo) } //updateObservationRegion send notify while update observation's region func updateObservationRegion(region string) { notification := models.BackNotification{Time: time.Now(), Kind: "疫情", Content: "本地有新的接触者,请及时登录系统进行处理"} sendInfo := models.SendInfo{Region: []string{region}, Channel: []int{0, 1}, Notification: notification} notify.SendNotify(sendInfo) } //updateSituation make some special resolve while situation update func updateSituation(claims models.TokenClaims, oldObservationMap, newObservationMap map[string]interface{}) { if newObservationMap["health_situation"] != nil { newHealthSituation := int(newObservationMap["health_situation"].(float64)) oldHealthSituation := oldObservationMap["health_situation"].(int) // Update health situation change time and measure_situation if newHealthSituation != oldHealthSituation { newObservationMap["health_change_time"] = time.Now() newObservationMap["measure_situation"] = 0 } // Deal with contact to patient if oldHealthSituation == 2 && newHealthSituation == 1 { // Query sub contacts queryMap := []map[string]interface{}{{"contact_person": oldObservationMap["id"].(int)}} subContacts := models.ListField[models.BackObservation](queryMap, false, "id", "region") // Get reginos and assemble situation record var regions []string var subContactRecords []models.BackSituationRecord for _, subContact := range *subContacts { regions = append(regions, subContact["region"].(string)) subContactRecord := models.BackSituationRecord{ Observation: int(newObservationMap["id"].(float64)), Record: record___20(), } subContactRecords = append(subContactRecords, subContactRecord) } // Update sub contact updateMap := map[string]interface{}{ "health_situation": 2, "health_change_time": newObservationMap["health_change_time"], "measure_situation": 0, } models.Update[models.BackObservation](queryMap, updateMap) // Insert subContacts' record var recordJMaps []map[string]interface{} utils.Strcut2Map(subContactRecords, &recordJMaps) recordCMap := models.MapsJ2c[models.BackSituationRecord](recordJMaps, true) models.BeforeBatchSave(&recordCMap, claims.ID) models.BatchInsert[models.BackSituationRecord](recordCMap) // Send nofity regions = utils.Distinct(regions) notification := models.BackNotification{ Time: newObservationMap["health_change_time"].(time.Time), Kind: "疫情", Content: "由于密接者转为患者,次密接者状态需要修改", } sendInfo := models.SendInfo{ Region: regions, Channel: []int{0, 1}, Notification: notification, } notify.SendNotify(sendInfo) } insertSituationRecord(claims, oldObservationMap, newObservationMap) } } //insertSituationRecord insert situation record func insertSituationRecord(claims models.TokenClaims, oldObservation, newObservation map[string]interface{}) bool { var oldSituation, newSituation string if oldObservation["id"] == nil { // insert observation oldSituation = "00" } else { // update observation oldSituation = fmt.Sprintf("%d%d", oldObservation["health_situation"], oldObservation["measure_situation"]) } if newObservation["health_situation"] == nil || newObservation["measure_situation"] == nil { // Situation not update return true } newSituation = fmt.Sprintf("%d%d", newObservation["health_situation"], newObservation["measure_situation"]) // Situation not update if oldSituation == newSituation { return true } // Set record content accrodding the situation convertions var recordContent string situationConvert := oldSituation + "_" + newSituation switch situationConvert { case "00_10": recordContent = record00_10() case "00_30": recordContent = record00_30() case "00_12": recordContent = record00_12(newObservation["region"].(string), newObservation["address"].(string)) case "10_12": recordContent = record10_12(newObservation["region"].(string), newObservation["address"].(string)) case "00_20", "33_20", "34_20": recordContent = record___20() case "12_01", "23_01", "24_01", "33_01", "34_01": recordContent = record___01() case "20_23", "30_33", "24_23", "34_33": recordContent = record____3(newObservation["region"].(string), newObservation["address"].(string)) case "20_24", "30_34", "23_24", "33_34": recordContent = record____4(newObservation["region"].(string), newObservation["address"].(string)) case "23_10", "24_10", "33_10", "34_10": recordContent = record___10() default: if oldSituation != "00" { models.Upsert[models.BackObservation](oldObservation) } else { models.DropById[models.BackObservation](newObservation["id"].(int)) } return false } record := models.BackSituationRecord{Observation: newObservation["id"].(int), Record: recordContent} var recordJMap map[string]interface{} utils.Strcut2Map(record, &recordJMap) recordCMap := models.MapJ2c[models.BackSituationRecord](recordJMap, true) models.BeforeSave(recordCMap, claims.ID) models.Upsert[models.BackSituationRecord](recordCMap) return true } func record00_10() string { date := utils.FormatDate(time.Now()) return date + " 确诊为患者" } func record00_12(region, address string) string { date := utils.FormatDate(time.Now()) return fmt.Sprintf("%s 于 %s %s 确诊为患者", date, region, address) } func record___20() string { date := utils.FormatDate(time.Now()) return date + " 转为密接" } func record00_30() string { date := utils.FormatDate(time.Now()) return date + " 转为次密接" } func record___01() string { date := utils.FormatDate(time.Now()) return fmt.Sprintf("%s 解除风险", date) } func record10_12(region, address string) string { date := utils.FormatDate(time.Now()) return fmt.Sprintf("%s 转至医院:%s %s", date, region, address) } func record___10() string { date := utils.FormatDate(time.Now()) return date + " 确诊为患者" } func record____3(region, address string) string { date := utils.FormatDate(time.Now()) return fmt.Sprintf("%s 集中隔离至:%s %s", date, region, address) } func record____4(region, address string) string { date := utils.FormatDate(time.Now()) return fmt.Sprintf("%s 进行居家隔离:%s %s", date, region, address) }