chore: 同步本地 v9 整改与运营能力
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user