627 lines
20 KiB
Go
627 lines
20 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"silk-server-go/internal/middleware"
|
|
"silk-server-go/internal/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
// RegisterDetectionTaskRoutes 注册统一检测任务、样本和发病事件路由。
|
|
func RegisterDetectionTaskRoutes(rg *gin.RouterGroup, db *gorm.DB) {
|
|
lampRead := middleware.RequirePermission(db, "lamp:read")
|
|
lampWrite := middleware.RequirePermission(db, "lamp:write")
|
|
traceRead := middleware.RequirePermission(db, "trace:read")
|
|
traceWrite := middleware.RequirePermission(db, "trace:write")
|
|
|
|
rg.GET("/detection-tasks", lampRead, listDetectionTasks(db))
|
|
rg.POST("/detection-tasks", lampWrite, createDetectionTask(db))
|
|
rg.GET("/detection-tasks/:id", lampRead, getDetectionTask(db))
|
|
rg.PATCH("/detection-tasks/:id", lampWrite, updateDetectionTask(db))
|
|
rg.GET("/detection-tasks/:id/samples", lampRead, listSamples(db))
|
|
rg.POST("/detection-tasks/:id/samples", lampWrite, createSample(db))
|
|
rg.PATCH("/samples/:id", lampWrite, updateSample(db))
|
|
rg.GET("/disease-events", traceRead, listDiseaseEvents(db))
|
|
rg.POST("/disease-events", traceWrite, createDiseaseEvent(db))
|
|
rg.PATCH("/disease-events/:id", traceWrite, updateDiseaseEvent(db))
|
|
}
|
|
|
|
func listDetectionTasks(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
q := applyRoomScope(db.Model(&model.DetectionTask{}), c, "room_id")
|
|
if status := c.Query("status"); status != "" {
|
|
q = q.Where("status = ?", status)
|
|
}
|
|
if room := c.Query("roomId"); room != "" {
|
|
q = q.Where("room_id = ?", room)
|
|
}
|
|
if sourceType := c.Query("sourceType"); sourceType != "" {
|
|
q = q.Where("source_type = ?", sourceType)
|
|
}
|
|
var list []model.DetectionTask
|
|
q.Order("created_at DESC").Limit(200).Find(&list)
|
|
fillDetectionTaskRoomNames(db, list)
|
|
c.JSON(http.StatusOK, list)
|
|
}
|
|
}
|
|
|
|
func getDetectionTask(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if !requireObjectAccess(c, db, "detection_task", c.Param("id")) {
|
|
return
|
|
}
|
|
var task model.DetectionTask
|
|
if db.Where("id = ?", c.Param("id")).First(&task).Error != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "detection task not found"})
|
|
return
|
|
}
|
|
fillDetectionTaskRoomNames(db, []model.DetectionTask{task})
|
|
c.JSON(http.StatusOK, task)
|
|
}
|
|
}
|
|
|
|
func createDetectionTask(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
var body struct {
|
|
SourceKey string `json:"sourceKey"`
|
|
SourceType string `json:"sourceType"`
|
|
SourceID string `json:"sourceId"`
|
|
RoomID *string `json:"roomId"`
|
|
BatchID *string `json:"batchId"`
|
|
InspectionID *string `json:"inspectionId"`
|
|
Disease string `json:"disease"`
|
|
RecommendedMethod string `json:"recommendedMethod"`
|
|
Method *string `json:"method"`
|
|
Priority string `json:"priority"`
|
|
Note *string `json:"note"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if body.Disease == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "病种不能为空"})
|
|
return
|
|
}
|
|
if body.Method != nil && !model.ValidDetectionMethod(*body.Method) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "method 仅支持 lamp/qpcr/sers/hyperspectral"})
|
|
return
|
|
}
|
|
for _, id := range []*string{body.RoomID, body.BatchID, body.InspectionID} {
|
|
if id != nil && *id != "" && !isUUID(*id) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "关联 ID 不是合法的 UUID"})
|
|
return
|
|
}
|
|
}
|
|
if body.RoomID != nil && !canAccessRoom(db, c, body.RoomID) {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "无权在该蚕房下创建检测任务"})
|
|
return
|
|
}
|
|
if body.BatchID != nil && !requireObjectAccess(c, db, "batch", *body.BatchID) {
|
|
return
|
|
}
|
|
if body.InspectionID != nil && !requireObjectAccess(c, db, "inspection", *body.InspectionID) {
|
|
return
|
|
}
|
|
sourceKey := body.SourceKey
|
|
if sourceKey == "" {
|
|
sourceKey = fmt.Sprintf("manual-%d", time.Now().UnixNano())
|
|
}
|
|
sourceType := body.SourceType
|
|
if sourceType == "" {
|
|
sourceType = "manual"
|
|
}
|
|
priority := body.Priority
|
|
if priority == "" {
|
|
priority = "routine"
|
|
}
|
|
task := model.DetectionTask{
|
|
SourceKey: sourceKey,
|
|
SourceType: sourceType,
|
|
SourceID: body.SourceID,
|
|
RoomID: body.RoomID,
|
|
BatchID: body.BatchID,
|
|
InspectionID: body.InspectionID,
|
|
Disease: body.Disease,
|
|
RecommendedMethod: body.RecommendedMethod,
|
|
Method: body.Method,
|
|
Priority: priority,
|
|
Status: "pending",
|
|
CreatedBy: currentUserID(c),
|
|
Note: body.Note,
|
|
}
|
|
if err := db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "source_key"}},
|
|
DoNothing: true,
|
|
}).Create(&task).Error; err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "创建检测任务失败"})
|
|
return
|
|
}
|
|
if task.ID == "" {
|
|
_ = db.Where("source_key = ?", sourceKey).First(&task).Error
|
|
}
|
|
c.JSON(http.StatusCreated, task)
|
|
}
|
|
}
|
|
|
|
func updateDetectionTask(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if !requireObjectAccess(c, db, "detection_task", c.Param("id")) {
|
|
return
|
|
}
|
|
var task model.DetectionTask
|
|
if db.Where("id = ?", c.Param("id")).First(&task).Error != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "detection task not found"})
|
|
return
|
|
}
|
|
var body struct {
|
|
AssigneeID *string `json:"assigneeId"`
|
|
Method *string `json:"method"`
|
|
Status *string `json:"status"`
|
|
Result *string `json:"result"`
|
|
CancelledReason *string `json:"cancelledReason"`
|
|
Note *string `json:"note"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if body.Method != nil && !model.ValidDetectionMethod(*body.Method) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "method 仅支持 lamp/qpcr/sers/hyperspectral"})
|
|
return
|
|
}
|
|
if body.Status != nil && !model.ValidDetectionTaskTransition(task.Status, *body.Status) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("检测任务状态不能从 %s 转到 %s", task.Status, *body.Status)})
|
|
return
|
|
}
|
|
if body.Status != nil && *body.Status == "assigned" && body.AssigneeID == nil && task.AssigneeID == nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "指派状态必须先分配负责人"})
|
|
return
|
|
}
|
|
if body.Status != nil && *body.Status == "completed" && body.Result == nil && task.Result == nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "完成检测任务前必须录入结果"})
|
|
return
|
|
}
|
|
if body.Status != nil && *body.Status == "cancelled" && (body.CancelledReason == nil || strings.TrimSpace(*body.CancelledReason) == "") {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "取消任务必须填写原因"})
|
|
return
|
|
}
|
|
updates := map[string]interface{}{}
|
|
if body.AssigneeID != nil {
|
|
updates["assignee_id"] = *body.AssigneeID
|
|
updates["assigned_at"] = time.Now()
|
|
}
|
|
if body.Method != nil {
|
|
updates["method"] = *body.Method
|
|
}
|
|
if body.Note != nil {
|
|
updates["note"] = *body.Note
|
|
}
|
|
if body.Result != nil {
|
|
if task.Status != "review" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "结果需在复核阶段录入"})
|
|
return
|
|
}
|
|
updates["result"] = *body.Result
|
|
updates["resulted_at"] = time.Now()
|
|
}
|
|
if body.Status != nil {
|
|
updates["status"] = *body.Status
|
|
if *body.Status == "cancelled" {
|
|
updates["cancelled_reason"] = *body.CancelledReason
|
|
}
|
|
}
|
|
if len(updates) > 0 {
|
|
if err := db.Model(&model.DetectionTask{}).Where("id = ?", task.ID).Updates(updates).Error; err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "更新检测任务失败"})
|
|
return
|
|
}
|
|
}
|
|
db.Where("id = ?", task.ID).First(&task)
|
|
if task.Result != nil && *task.Result == "positive" {
|
|
_ = ensureDiseaseEventFromDetectionTask(db, task)
|
|
}
|
|
fillDetectionTaskRoomNames(db, []model.DetectionTask{task})
|
|
c.JSON(http.StatusOK, task)
|
|
}
|
|
}
|
|
|
|
func listSamples(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if !requireObjectAccess(c, db, "detection_task", c.Param("id")) {
|
|
return
|
|
}
|
|
var samples []model.Sample
|
|
db.Where("detection_task_id = ?", c.Param("id")).Order("created_at ASC").Find(&samples)
|
|
c.JSON(http.StatusOK, samples)
|
|
}
|
|
}
|
|
|
|
func createSample(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if !requireObjectAccess(c, db, "detection_task", c.Param("id")) {
|
|
return
|
|
}
|
|
var task model.DetectionTask
|
|
if db.Where("id = ?", c.Param("id")).First(&task).Error != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "detection task not found"})
|
|
return
|
|
}
|
|
var existing model.Sample
|
|
if db.Where("detection_task_id = ?", task.ID).First(&existing).Error == nil {
|
|
c.JSON(http.StatusOK, existing)
|
|
return
|
|
}
|
|
var body struct {
|
|
SampleNo string `json:"sampleNo"`
|
|
RoomID *string `json:"roomId"`
|
|
BatchID *string `json:"batchId"`
|
|
TrayID *string `json:"trayId"`
|
|
Note *string `json:"note"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
sampleNo := body.SampleNo
|
|
if sampleNo == "" {
|
|
sampleNo = fmt.Sprintf("S-%s", strings.ToUpper(task.ID[:8]))
|
|
}
|
|
now := time.Now()
|
|
sample := model.Sample{
|
|
DetectionTaskID: task.ID,
|
|
SampleNo: sampleNo,
|
|
RoomID: body.RoomID,
|
|
BatchID: body.BatchID,
|
|
TrayID: body.TrayID,
|
|
SampledBy: currentUserID(c),
|
|
SampledAt: &now,
|
|
State: "created",
|
|
Note: body.Note,
|
|
}
|
|
if sample.RoomID == nil {
|
|
sample.RoomID = task.RoomID
|
|
}
|
|
if sample.BatchID == nil {
|
|
sample.BatchID = task.BatchID
|
|
}
|
|
if err := db.Create(&sample).Error; err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "创建样本失败"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, sample)
|
|
}
|
|
}
|
|
|
|
func updateSample(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if !requireObjectAccess(c, db, "sample", c.Param("id")) {
|
|
return
|
|
}
|
|
var sample model.Sample
|
|
if db.Where("id = ?", c.Param("id")).First(&sample).Error != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "sample not found"})
|
|
return
|
|
}
|
|
var body struct {
|
|
State *string `json:"state"`
|
|
Note *string `json:"note"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
updates := map[string]interface{}{}
|
|
if body.Note != nil {
|
|
updates["note"] = *body.Note
|
|
}
|
|
if body.State != nil {
|
|
if !model.ValidSampleTransition(sample.State, *body.State) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("样本状态不能从 %s 转到 %s", sample.State, *body.State)})
|
|
return
|
|
}
|
|
updates["state"] = *body.State
|
|
now := time.Now()
|
|
switch *body.State {
|
|
case "collected":
|
|
updates["collected_at"] = now
|
|
case "handed_over":
|
|
updates["handed_over_at"] = now
|
|
updates["handed_over_by"] = currentUserID(c)
|
|
case "received":
|
|
updates["received_at"] = now
|
|
updates["received_by"] = currentUserID(c)
|
|
case "testing":
|
|
updates["testing_started_at"] = now
|
|
case "consumed":
|
|
updates["consumed_at"] = now
|
|
case "disposed":
|
|
updates["disposed_at"] = now
|
|
}
|
|
}
|
|
if len(updates) > 0 {
|
|
if err := db.Model(&model.Sample{}).Where("id = ?", sample.ID).Updates(updates).Error; err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "更新样本失败"})
|
|
return
|
|
}
|
|
}
|
|
db.Where("id = ?", sample.ID).First(&sample)
|
|
c.JSON(http.StatusOK, sample)
|
|
}
|
|
}
|
|
|
|
func listDiseaseEvents(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
q := applyRoomScope(db.Model(&model.DiseaseEvent{}), c, "room_id")
|
|
if status := c.Query("status"); status != "" {
|
|
q = q.Where("status = ?", status)
|
|
}
|
|
if room := c.Query("roomId"); room != "" {
|
|
q = q.Where("room_id = ?", room)
|
|
}
|
|
var list []model.DiseaseEvent
|
|
q.Order("created_at DESC").Limit(200).Find(&list)
|
|
fillDiseaseEventRoomNames(db, list)
|
|
c.JSON(http.StatusOK, list)
|
|
}
|
|
}
|
|
|
|
func createDiseaseEvent(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
var body struct {
|
|
SourceKey string `json:"sourceKey"`
|
|
RoomID *string `json:"roomId"`
|
|
BatchID *string `json:"batchId"`
|
|
DetectionTaskID *string `json:"detectionTaskId"`
|
|
LampTestID *string `json:"lampTestId"`
|
|
ConsultationID *string `json:"consultationId"`
|
|
InspectionID *string `json:"inspectionId"`
|
|
Disease string `json:"disease"`
|
|
Status string `json:"status"`
|
|
Evidence json.RawMessage `json:"evidence"`
|
|
LossSummary *string `json:"lossSummary"`
|
|
Measure *string `json:"measure"`
|
|
Note *string `json:"note"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if body.Disease == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "病种不能为空"})
|
|
return
|
|
}
|
|
for _, id := range []*string{body.RoomID, body.BatchID, body.DetectionTaskID, body.LampTestID, body.ConsultationID, body.InspectionID} {
|
|
if id != nil && *id != "" && !isUUID(*id) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "关联 ID 不是合法的 UUID"})
|
|
return
|
|
}
|
|
}
|
|
if body.RoomID != nil && !canAccessRoom(db, c, body.RoomID) {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "无权在该蚕房下创建发病事件"})
|
|
return
|
|
}
|
|
if body.BatchID != nil && !requireObjectAccess(c, db, "batch", *body.BatchID) {
|
|
return
|
|
}
|
|
if body.DetectionTaskID != nil && !requireObjectAccess(c, db, "detection_task", *body.DetectionTaskID) {
|
|
return
|
|
}
|
|
if body.LampTestID != nil && !requireObjectAccess(c, db, "lamp_test", *body.LampTestID) {
|
|
return
|
|
}
|
|
if body.ConsultationID != nil && !requireObjectAccess(c, db, "consultation", *body.ConsultationID) {
|
|
return
|
|
}
|
|
if body.InspectionID != nil && !requireObjectAccess(c, db, "inspection", *body.InspectionID) {
|
|
return
|
|
}
|
|
if body.Status == "" {
|
|
body.Status = "suspected"
|
|
}
|
|
event := model.DiseaseEvent{
|
|
SourceKey: body.SourceKey,
|
|
RoomID: body.RoomID,
|
|
BatchID: body.BatchID,
|
|
DetectionTaskID: body.DetectionTaskID,
|
|
LampTestID: body.LampTestID,
|
|
ConsultationID: body.ConsultationID,
|
|
InspectionID: body.InspectionID,
|
|
Disease: body.Disease,
|
|
Status: body.Status,
|
|
Evidence: body.Evidence,
|
|
LossSummary: body.LossSummary,
|
|
Measure: body.Measure,
|
|
Note: body.Note,
|
|
}
|
|
if event.SourceKey == "" {
|
|
event.SourceKey = fmt.Sprintf("manual-%d", time.Now().UnixNano())
|
|
}
|
|
if err := model.ValidateDiseaseEventEvidence(event); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if event.Status == "confirmed" {
|
|
now := time.Now()
|
|
event.ConfirmedAt = &now
|
|
event.ConfirmedBy = currentUserID(c)
|
|
}
|
|
if err := db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "source_key"}},
|
|
DoNothing: true,
|
|
}).Create(&event).Error; err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "创建发病事件失败"})
|
|
return
|
|
}
|
|
if event.ID == "" {
|
|
_ = db.Where("source_key = ?", event.SourceKey).First(&event).Error
|
|
}
|
|
if event.Status == "confirmed" {
|
|
_ = ensureTraceForDiseaseEvent(db, event)
|
|
}
|
|
c.JSON(http.StatusCreated, event)
|
|
}
|
|
}
|
|
|
|
func updateDiseaseEvent(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if !requireObjectAccess(c, db, "disease_event", c.Param("id")) {
|
|
return
|
|
}
|
|
var event model.DiseaseEvent
|
|
if db.Where("id = ?", c.Param("id")).First(&event).Error != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "disease event not found"})
|
|
return
|
|
}
|
|
var body struct {
|
|
Status *string `json:"status"`
|
|
Evidence json.RawMessage `json:"evidence"`
|
|
LossSummary *string `json:"lossSummary"`
|
|
Measure *string `json:"measure"`
|
|
Note *string `json:"note"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
updates := map[string]interface{}{}
|
|
if body.Status != nil {
|
|
if !model.ValidDiseaseEventTransition(event.Status, *body.Status) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("发病事件状态不能从 %s 转到 %s", event.Status, *body.Status)})
|
|
return
|
|
}
|
|
updates["status"] = *body.Status
|
|
if *body.Status == "confirmed" {
|
|
if len(body.Evidence) == 0 && len(event.Evidence) == 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "确诊发病事件必须提供证据"})
|
|
return
|
|
}
|
|
now := time.Now()
|
|
updates["confirmed_at"] = now
|
|
updates["confirmed_by"] = currentUserID(c)
|
|
}
|
|
}
|
|
if len(body.Evidence) > 0 {
|
|
updates["evidence"] = body.Evidence
|
|
}
|
|
if body.LossSummary != nil {
|
|
updates["loss_summary"] = *body.LossSummary
|
|
}
|
|
if body.Measure != nil {
|
|
updates["measure"] = *body.Measure
|
|
}
|
|
if body.Note != nil {
|
|
updates["note"] = *body.Note
|
|
}
|
|
if len(updates) > 0 {
|
|
if err := db.Model(&model.DiseaseEvent{}).Where("id = ?", event.ID).Updates(updates).Error; err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "更新发病事件失败"})
|
|
return
|
|
}
|
|
}
|
|
db.Where("id = ?", event.ID).First(&event)
|
|
if event.Status == "confirmed" {
|
|
_ = ensureTraceForDiseaseEvent(db, event)
|
|
}
|
|
c.JSON(http.StatusOK, event)
|
|
}
|
|
}
|
|
|
|
func fillDetectionTaskRoomNames(db *gorm.DB, list []model.DetectionTask) {
|
|
var rooms []model.Room
|
|
db.Select("id", "name").Find(&rooms)
|
|
names := make(map[string]string, len(rooms))
|
|
for _, r := range rooms {
|
|
names[r.ID] = r.Name
|
|
}
|
|
for i := range list {
|
|
if list[i].RoomID != nil {
|
|
if name, ok := names[*list[i].RoomID]; ok {
|
|
list[i].RoomName = &name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func fillDiseaseEventRoomNames(db *gorm.DB, list []model.DiseaseEvent) {
|
|
var rooms []model.Room
|
|
db.Select("id", "name").Find(&rooms)
|
|
names := make(map[string]string, len(rooms))
|
|
for _, r := range rooms {
|
|
names[r.ID] = r.Name
|
|
}
|
|
for i := range list {
|
|
if list[i].RoomID != nil {
|
|
if name, ok := names[*list[i].RoomID]; ok {
|
|
list[i].RoomName = &name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ensureTraceForDiseaseEvent 确诊后自动创建关联溯源记录,重复调用不重复创建。
|
|
func ensureTraceForDiseaseEvent(db *gorm.DB, event model.DiseaseEvent) error {
|
|
if event.Status != "confirmed" {
|
|
return nil
|
|
}
|
|
var count int64
|
|
if err := db.Model(&model.TraceRecord{}).Where("disease_event_id = ?", event.ID).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return nil
|
|
}
|
|
trace := model.TraceRecord{
|
|
DiseaseEventID: &event.ID,
|
|
RoomID: event.RoomID,
|
|
LampTestID: event.LampTestID,
|
|
Disease: event.Disease,
|
|
Status: "pending",
|
|
}
|
|
return db.Create(&trace).Error
|
|
}
|
|
|
|
func ensureDiseaseEventFromDetectionTask(db *gorm.DB, task model.DetectionTask) error {
|
|
if task.Result == nil || *task.Result != "positive" {
|
|
return nil
|
|
}
|
|
now := time.Now()
|
|
evidence, _ := json.Marshal(map[string]interface{}{
|
|
"result": *task.Result,
|
|
"method": task.Method,
|
|
"detectionTaskId": task.ID,
|
|
"resultedAt": task.ResultedAt,
|
|
})
|
|
sourceKey := "detection-task-" + task.ID
|
|
event := model.DiseaseEvent{
|
|
SourceKey: sourceKey,
|
|
RoomID: task.RoomID,
|
|
BatchID: task.BatchID,
|
|
DetectionTaskID: &task.ID,
|
|
InspectionID: task.InspectionID,
|
|
Disease: task.Disease,
|
|
Status: "confirmed",
|
|
Evidence: evidence,
|
|
ConfirmedAt: &now,
|
|
ConfirmedBy: task.AssigneeID,
|
|
}
|
|
if err := db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "source_key"}},
|
|
DoNothing: true,
|
|
}).Create(&event).Error; err != nil {
|
|
return err
|
|
}
|
|
if event.ID == "" {
|
|
_ = db.Where("source_key = ?", sourceKey).First(&event).Error
|
|
}
|
|
return ensureTraceForDiseaseEvent(db, event)
|
|
}
|