package handler import ( "net/http" "time" "silk-server-go/internal/middleware" "silk-server-go/internal/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // RegisterLaboratoryRoutes 注册实验室结构化结果和分子分型路由。 func RegisterLaboratoryRoutes(rg *gin.RouterGroup, db *gorm.DB) { read := middleware.RequirePermission(db, "lab:read") write := middleware.RequirePermission(db, "lab:write") rg.GET("/laboratory-results", read, listLaboratoryResults(db)) rg.POST("/laboratory-results", write, createLaboratoryResult(db)) rg.PATCH("/laboratory-results/:id", write, updateLaboratoryResult(db)) rg.DELETE("/laboratory-results/:id", write, deleteLaboratoryResult(db)) } func listLaboratoryResults(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { q := db.Model(&model.LaboratoryResult{}). Joins("LEFT JOIN batches b ON b.id = laboratory_results.batch_id"). Joins("LEFT JOIN trace_records tr ON tr.id = laboratory_results.trace_record_id") q = applyRoomScope(q, c, "COALESCE(laboratory_results.room_id, b.room_id, tr.room_id)") if traceID := c.Query("traceRecordId"); traceID != "" { q = q.Where("laboratory_results.trace_record_id = ?", traceID) } if diseaseEventID := c.Query("diseaseEventId"); diseaseEventID != "" { q = q.Where("laboratory_results.disease_event_id = ?", diseaseEventID) } if roomID := c.Query("roomId"); roomID != "" { q = q.Where("laboratory_results.room_id = ?", roomID) } var list []model.LaboratoryResult q.Order("laboratory_results.tested_at DESC NULLS LAST, laboratory_results.created_at DESC").Limit(200).Find(&list) c.JSON(http.StatusOK, list) } } func createLaboratoryResult(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { var body struct { TraceRecordID *string `json:"traceRecordId"` DiseaseEventID *string `json:"diseaseEventId"` SampleID *string `json:"sampleId"` RoomID *string `json:"roomId"` BatchID *string `json:"batchId"` LabName string `json:"labName"` ReportNo string `json:"reportNo"` TestType string `json:"testType"` ResultType string `json:"resultType"` Pathogen *string `json:"pathogen"` Genotype *string `json:"genotype"` Method *string `json:"method"` SampleNo *string `json:"sampleNo"` SampleType *string `json:"sampleType"` Findings *string `json:"findings"` ReportURL *string `json:"reportUrl"` TestedAt *time.Time `json:"testedAt"` ConcludedAt *time.Time `json:"concludedAt"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } for _, id := range []*string{body.TraceRecordID, body.DiseaseEventID, body.SampleID, body.RoomID, body.BatchID} { if id != nil && *id != "" && !isUUID(*id) { c.JSON(http.StatusBadRequest, gin.H{"error": "关联 ID 不是合法的 UUID"}) return } } if body.TraceRecordID != nil && !requireObjectAccess(c, db, "trace_record", *body.TraceRecordID) { return } if body.DiseaseEventID != nil && !requireObjectAccess(c, db, "disease_event", *body.DiseaseEventID) { return } if body.SampleID != nil && !requireObjectAccess(c, db, "sample", *body.SampleID) { 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 } result := model.LaboratoryResult{ TraceRecordID: body.TraceRecordID, DiseaseEventID: body.DiseaseEventID, SampleID: body.SampleID, RoomID: body.RoomID, BatchID: body.BatchID, LabName: body.LabName, ReportNo: body.ReportNo, TestType: body.TestType, ResultType: body.ResultType, Pathogen: body.Pathogen, Genotype: body.Genotype, Method: body.Method, SampleNo: body.SampleNo, SampleType: body.SampleType, Findings: body.Findings, ReportURL: body.ReportURL, TestedAt: body.TestedAt, ConcludedAt: body.ConcludedAt, CreatedBy: currentUserID(c), } if result.RoomID == nil && body.TraceRecordID != nil { var trace model.TraceRecord if db.Where("id = ?", *body.TraceRecordID).First(&trace).Error == nil { result.RoomID = trace.RoomID if result.BatchID == nil { result.BatchID = batchFromTrace(db, trace) } } } if result.RoomID == nil && body.SampleID != nil { var sample model.Sample if db.Where("id = ?", *body.SampleID).First(&sample).Error == nil { result.RoomID = sample.RoomID if result.BatchID == nil { result.BatchID = sample.BatchID } } } if err := model.ValidateLaboratoryResult(result); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := db.Create(&result).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "创建实验室结果失败"}) return } c.JSON(http.StatusCreated, result) } } func updateLaboratoryResult(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") if !requireObjectAccess(c, db, "laboratory_result", id) { return } var result model.LaboratoryResult if db.Where("id = ?", id).First(&result).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "laboratory result not found"}) return } updates, err := bindUpdates(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if roomID, ok := updates["room_id"].(string); ok && !canAccessRoom(db, c, &roomID) { c.JSON(http.StatusForbidden, gin.H{"error": "无权迁移该结果到指定蚕房"}) return } for _, ref := range []struct { key string kind string }{ {key: "trace_record_id", kind: "trace_record"}, {key: "disease_event_id", kind: "disease_event"}, {key: "sample_id", kind: "sample"}, {key: "batch_id", kind: "batch"}, } { if id, ok := updates[ref.key].(string); ok && !requireObjectAccess(c, db, ref.kind, id) { return } } if len(updates) > 0 { if err := db.Model(&model.LaboratoryResult{}).Where("id = ?", id).Updates(updates).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "更新实验室结果失败"}) return } } db.Where("id = ?", id).First(&result) c.JSON(http.StatusOK, result) } } func deleteLaboratoryResult(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") if !requireObjectAccess(c, db, "laboratory_result", id) { return } var result model.LaboratoryResult if db.Where("id = ?", id).First(&result).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "laboratory result not found"}) return } db.Where("id = ?", id).Delete(&model.LaboratoryResult{}) c.JSON(http.StatusOK, gin.H{"id": id}) } } func batchFromTrace(db *gorm.DB, trace model.TraceRecord) *string { if trace.LampTestID != nil { var lamp model.LampTest if db.Where("id = ?", *trace.LampTestID).First(&lamp).Error == nil { return lamp.BatchID } } return nil }