feat: 交叉验证 AI vs LAMP(#15,一致确认/不一致升级会诊)

This commit is contained in:
weijuesen
2026-08-12 18:00:02 +08:00
parent 6f367d19ae
commit 1798b2909e
9 changed files with 199 additions and 0 deletions
+75
View File
@@ -2,6 +2,7 @@ package handler
import (
"bytes"
"encoding/json"
"io"
"net/http"
"strconv"
@@ -26,6 +27,7 @@ func RegisterLampRoutes(rg *gin.RouterGroup, db *gorm.DB, s3 *service.S3Service,
rg.GET("/lamp-tests/:id/steps", read, listLampTestSteps(db))
rg.PATCH("/lamp-tests/:id/steps/:stepNo", write, updateLampTestStep(db))
rg.POST("/lamp-tests/:id/result-image", write, uploadLampResultImage(db, s3, imageBucket))
rg.GET("/lamp-tests/:id/cross-validation", read, getLampCrossValidation(db))
}
// listLampTests 检测任务单列表(roomId/batchId/status 过滤)
@@ -97,6 +99,8 @@ func updateLampTest(db *gorm.DB) gin.HandlerFunc {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
resultSet := false
resultValue := ""
if r, ok := updates["result"]; ok {
result, _ := r.(string)
if result != "" && !model.ValidLampResult(result) {
@@ -109,15 +113,86 @@ func updateLampTest(db *gorm.DB) gin.HandlerFunc {
} else {
updates["status"] = "testing"
}
resultSet = true
resultValue = result
}
if len(updates) > 0 {
db.Model(&model.LampTest{}).Where("id = ?", id).Updates(updates)
}
// 交叉验证:结果落库后与同房间最近巡检比对
if resultSet && resultValue != "" {
_ = runCrossValidation(db, id, resultValue)
}
db.Where("id = ?", id).First(&t)
c.JSON(http.StatusOK, t)
}
}
// runCrossValidation 用同房间最近的巡检记录做交叉验证,并回写 cross_status/cross_reason
func runCrossValidation(db *gorm.DB, lampTestID, lampResult string) error {
var t model.LampTest
if err := db.Where("id = ?", lampTestID).First(&t).Error; err != nil {
return err
}
if t.RoomID == nil {
return nil
}
var inspection model.InspectionRecord
if err := db.Where("room_id = ?", *t.RoomID).Order("created_at DESC").First(&inspection).Error; err != nil {
return nil // 无关联巡检,保持 pending
}
var detections []service.AIDetection
if len(inspection.Detections) > 0 {
_ = json.Unmarshal(inspection.Detections, &detections)
}
aiClass := service.AIClassFromDetections(detections)
var diseases []string
if len(t.Diseases) > 0 {
_ = json.Unmarshal(t.Diseases, &diseases)
}
ok, reason := service.CrossValidate(aiClass, lampResult, diseases)
status := "inconsistent"
if ok {
status = "consistent"
}
return db.Model(&model.LampTest{}).Where("id = ?", lampTestID).
Updates(map[string]interface{}{"cross_status": status, "cross_reason": reason}).Error
}
// getLampCrossValidation 交叉验证详情(含关联巡检摘要)
func getLampCrossValidation(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var t model.LampTest
if db.Where("id = ?", id).First(&t).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "lamp test not found"})
return
}
var related map[string]interface{}
if t.RoomID != nil {
var inspection model.InspectionRecord
if db.Where("room_id = ?", *t.RoomID).Order("created_at DESC").First(&inspection).Error == nil {
var detections []service.AIDetection
if len(inspection.Detections) > 0 {
_ = json.Unmarshal(inspection.Detections, &detections)
}
related = map[string]interface{}{
"id": inspection.ID,
"imageUrl": inspection.ImageURL,
"aiClass": service.AIClassFromDetections(detections),
"riskLevel": inspection.RiskLevel,
"createdAt": inspection.CreatedAt,
}
}
}
c.JSON(http.StatusOK, gin.H{
"crossStatus": t.CrossStatus,
"crossReason": t.CrossReason,
"relatedInspection": related,
})
}
}
// deleteLampTest 删除任务单(级联删除步骤)
func deleteLampTest(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {