package service import ( "sort" "strings" "time" ) // HealthInput 健康画像输入 type HealthInput struct { RecentRiskLevels []string // 近 30 天巡检风险等级 LampPositive int LampTotal int ConsultationCount int TraceCount int } // ComputeHealthScore 综合健康分(0-100)与评级(优/良/中/差) func ComputeHealthScore(in HealthInput) (float64, string) { score := 100.0 for _, level := range in.RecentRiskLevels { switch level { case "red": score -= 25 case "orange": score -= 12 case "yellow": score -= 4 } } if in.LampTotal > 0 { score -= float64(in.LampPositive) / float64(in.LampTotal) * 30 } score -= float64(in.ConsultationCount) * 10 score -= float64(in.TraceCount) * 8 if score < 0 { score = 0 } if score > 100 { score = 100 } grade := "差" switch { case score >= 85: grade = "优" case score >= 70: grade = "良" case score >= 55: grade = "中" } return score, grade } // MonthDiseaseEntry 月度发病条目 type MonthDiseaseEntry struct { Month string Disease string } // MonthStat 月度发病统计 type MonthStat struct { Month string `json:"month"` Total int `json:"total"` Diseases map[string]int `json:"diseases"` } // AggregateMonthlyStats 按月份聚合发病数与病种分布(升序) func AggregateMonthlyStats(entries []MonthDiseaseEntry) []MonthStat { byMonth := make(map[string]*MonthStat) for _, e := range entries { if strings.TrimSpace(e.Month) == "" { continue } s, ok := byMonth[e.Month] if !ok { s = &MonthStat{Month: e.Month, Diseases: map[string]int{}} byMonth[e.Month] = s } s.Total++ s.Diseases[e.Disease]++ } result := make([]MonthStat, 0, len(byMonth)) for _, s := range byMonth { result = append(result, *s) } sort.Slice(result, func(i, j int) bool { return result[i].Month < result[j].Month }) return result } // EffectWindow 处置前后评估窗口。 type EffectWindow struct { BeforeStart time.Time `json:"beforeStart"` BeforeEnd time.Time `json:"beforeEnd"` AfterStart time.Time `json:"afterStart"` AfterEnd time.Time `json:"afterEnd"` } // EffectMetrics 处置前后指标输入。 type EffectMetrics struct { RiskSamples []float64 `json:"riskSamples"` LampPositive int `json:"lampPositive"` LampTotal int `json:"lampTotal"` Recurrences int `json:"recurrences"` Loss *float64 `json:"loss,omitempty"` Cost *float64 `json:"cost,omitempty"` } // EffectReport 防控效果报告;缺失维度不按 0 当改善。 type EffectReport struct { EventID string `json:"eventId"` Window EffectWindow `json:"window"` BeforeRiskAvg *float64 `json:"beforeRiskAvg,omitempty"` AfterRiskAvg *float64 `json:"afterRiskAvg,omitempty"` BeforePositiveRate *float64 `json:"beforePositiveRate,omitempty"` AfterPositiveRate *float64 `json:"afterPositiveRate,omitempty"` Recurrences int `json:"recurrences"` Missing []string `json:"missing"` Conclusion string `json:"conclusion"` } // EvaluateControlEffect 对比指定窗口风险、阳性率和复发;缺失维度单独列出。 func EvaluateControlEffect(eventID string, window EffectWindow, before, after EffectMetrics) EffectReport { report := EffectReport{ EventID: eventID, Window: window, Recurrences: after.Recurrences, Missing: []string{}, } if len(before.RiskSamples) > 0 { value := avg(before.RiskSamples) report.BeforeRiskAvg = &value } else { report.Missing = append(report.Missing, "before_risk") } if len(after.RiskSamples) > 0 { value := avg(after.RiskSamples) report.AfterRiskAvg = &value } else { report.Missing = append(report.Missing, "after_risk") } if before.LampTotal > 0 { value := float64(before.LampPositive) / float64(before.LampTotal) report.BeforePositiveRate = &value } else { report.Missing = append(report.Missing, "before_lamp") } if after.LampTotal > 0 { value := float64(after.LampPositive) / float64(after.LampTotal) report.AfterPositiveRate = &value } else { report.Missing = append(report.Missing, "after_lamp") } if before.Loss == nil { report.Missing = append(report.Missing, "before_loss") } if after.Loss == nil { report.Missing = append(report.Missing, "after_loss") } if before.Cost == nil { report.Missing = append(report.Missing, "before_cost") } if after.Cost == nil { report.Missing = append(report.Missing, "after_cost") } switch { case report.AfterRiskAvg == nil || report.BeforeRiskAvg == nil: report.Conclusion = "证据不足,无法判断效果" case *report.AfterRiskAvg < *report.BeforeRiskAvg && after.Recurrences == 0: report.Conclusion = "改善" case *report.AfterRiskAvg >= *report.BeforeRiskAvg: report.Conclusion = "未改善" default: report.Conclusion = "需人工复核" } return report } func avg(values []float64) float64 { sum := 0.0 for _, v := range values { sum += v } return sum / float64(len(values)) }