390 lines
16 KiB
Go
390 lines
16 KiB
Go
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)
|
|
}
|