feat(server-go): 蚕房健康画像与年度发病统计(#24)
This commit is contained in:
@@ -2,13 +2,93 @@ package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"silk-server-go/internal/middleware"
|
||||
"silk-server-go/internal/model"
|
||||
"silk-server-go/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RegisterHealthRoutes 注册健康检查路由
|
||||
func RegisterHealthRoutes(rg *gin.RouterGroup) {
|
||||
rg.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
})
|
||||
// RegisterHealthRoutes 注册蚕房健康画像路由
|
||||
func RegisterHealthRoutes(rg *gin.RouterGroup, db *gorm.DB) {
|
||||
rg.GET("/rooms/:id/health-profile",
|
||||
middleware.RequirePermission(db, "room:read"),
|
||||
roomHealthProfile(db))
|
||||
}
|
||||
|
||||
// roomHealthProfile 单蚕房健康画像:巡检风险/检测结果/事件聚合 → 综合健康分
|
||||
func roomHealthProfile(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var room model.Room
|
||||
if db.Where("id = ?", id).First(&room).Error != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "room not found"})
|
||||
return
|
||||
}
|
||||
since := time.Now().Add(-30 * 24 * time.Hour)
|
||||
|
||||
// 近 30 天巡检风险分布
|
||||
var riskRows []struct {
|
||||
RiskLevel string `gorm:"column:risk_level"`
|
||||
Cnt int64 `gorm:"column:cnt"`
|
||||
}
|
||||
db.Table("inspection_records").
|
||||
Select("risk_level, count(*) AS cnt").
|
||||
Where("room_id = ? AND ai_status = 'done' AND risk_level IS NOT NULL AND created_at >= ?", id, since).
|
||||
Group("risk_level").
|
||||
Scan(&riskRows)
|
||||
riskCounts := map[string]int64{}
|
||||
var riskLevels []string
|
||||
for _, r := range riskRows {
|
||||
riskCounts[r.RiskLevel] = r.Cnt
|
||||
for i := int64(0); i < r.Cnt; i++ {
|
||||
riskLevels = append(riskLevels, r.RiskLevel)
|
||||
}
|
||||
}
|
||||
|
||||
// 近 30 天 LAMP/分子检测结果
|
||||
lampTotal, lampPositive := int64(0), int64(0)
|
||||
db.Model(&model.LampTest{}).
|
||||
Where("room_id = ? AND status = 'resulted' AND created_at >= ?", id, since).
|
||||
Count(&lampTotal)
|
||||
db.Model(&model.LampTest{}).
|
||||
Where("room_id = ? AND status = 'resulted' AND result = 'positive' AND created_at >= ?", id, since).
|
||||
Count(&lampPositive)
|
||||
|
||||
// 事件数
|
||||
var consultationCount, traceCount int64
|
||||
db.Model(&model.Consultation{}).Where("room_id = ?", id).Count(&consultationCount)
|
||||
db.Model(&model.TraceRecord{}).Where("room_id = ?", id).Count(&traceCount)
|
||||
|
||||
// 当前批次
|
||||
var batch *model.Batch
|
||||
var latest model.Batch
|
||||
if db.Where("room_id = ?", id).Order("created_at DESC").First(&latest).Error == nil {
|
||||
batch = &latest
|
||||
}
|
||||
|
||||
score, grade := service.ComputeHealthScore(service.HealthInput{
|
||||
RecentRiskLevels: riskLevels,
|
||||
LampPositive: int(lampPositive),
|
||||
LampTotal: int(lampTotal),
|
||||
ConsultationCount: int(consultationCount),
|
||||
TraceCount: int(traceCount),
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"roomId": room.ID,
|
||||
"roomName": room.Name,
|
||||
"score": score,
|
||||
"grade": grade,
|
||||
"riskDistribution": gin.H{
|
||||
"green": riskCounts["green"], "yellow": riskCounts["yellow"],
|
||||
"orange": riskCounts["orange"], "red": riskCounts["red"],
|
||||
},
|
||||
"lampResults": gin.H{"total": lampTotal, "positive": lampPositive},
|
||||
"events": gin.H{"consultations": consultationCount, "traces": traceCount},
|
||||
"batch": batch,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ func RegisterTraceRoutes(rg *gin.RouterGroup, db *gorm.DB) {
|
||||
write := middleware.RequirePermission(db, "trace:write")
|
||||
rg.GET("/trace-records", read, listTraceRecords(db))
|
||||
rg.GET("/trace-records/region-stats", read, traceRegionStats(db))
|
||||
rg.GET("/trace-records/monthly-stats", read, traceMonthlyStats(db))
|
||||
rg.GET("/trace-records/:id", read, getTraceRecord(db))
|
||||
rg.POST("/trace-records", write, createTraceRecord(db))
|
||||
rg.PATCH("/trace-records/:id", write, updateTraceRecord(db))
|
||||
@@ -29,6 +30,31 @@ func RegisterTraceRoutes(rg *gin.RouterGroup, db *gorm.DB) {
|
||||
rg.POST("/trace-records/:id/checklist", write, submitTraceChecklist(db))
|
||||
}
|
||||
|
||||
// traceMonthlyStats 年度发病规律(按月聚合)
|
||||
func traceMonthlyStats(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
year := time.Now().Year()
|
||||
if y, err := strconv.Atoi(c.DefaultQuery("year", "0")); err == nil && y >= 2020 && y <= 2100 {
|
||||
year = y
|
||||
}
|
||||
start := time.Date(year, 1, 1, 0, 0, 0, 0, time.Local)
|
||||
end := start.AddDate(1, 0, 0)
|
||||
var rows []struct {
|
||||
Month string `gorm:"column:month"`
|
||||
Disease string `gorm:"column:disease"`
|
||||
}
|
||||
db.Table("trace_records").
|
||||
Select("to_char(created_at, 'YYYY-MM') AS month, disease").
|
||||
Where("created_at >= ? AND created_at < ?", start, end).
|
||||
Scan(&rows)
|
||||
entries := make([]service.MonthDiseaseEntry, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
entries = append(entries, service.MonthDiseaseEntry{Month: r.Month, Disease: r.Disease})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"year": year, "stats": service.AggregateMonthlyStats(entries)})
|
||||
}
|
||||
}
|
||||
|
||||
// traceRegionStats 区域发病统计(近 N 天,按蚕房 region 聚合)
|
||||
func traceRegionStats(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
Reference in New Issue
Block a user