feat(server-go): 疫病溯源三级模型与接口(#21,一级自动溯源/二级清单/三级备注)
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EnvSample 环境回溯样本
|
||||
type EnvSample struct {
|
||||
Time time.Time
|
||||
Temp *float64
|
||||
Humidity *float64
|
||||
}
|
||||
|
||||
// EnvironmentResult 环境回溯结果
|
||||
type EnvironmentResult struct {
|
||||
HighHumidity bool
|
||||
TempSwing bool
|
||||
Summary string
|
||||
}
|
||||
|
||||
// EnvironmentBacktrack 发病前环境回溯:湿度≥阈值 或 温度>30/<20 触发(规格书 3.8.3)
|
||||
func EnvironmentBacktrack(samples []EnvSample, humidityThreshold float64) EnvironmentResult {
|
||||
r := EnvironmentResult{}
|
||||
if len(samples) == 0 {
|
||||
r.Summary = "无可用环境遥测数据"
|
||||
return r
|
||||
}
|
||||
for _, s := range samples {
|
||||
if s.Humidity != nil && *s.Humidity >= humidityThreshold {
|
||||
r.HighHumidity = true
|
||||
}
|
||||
if s.Temp != nil && (*s.Temp > 30 || *s.Temp < 20) {
|
||||
r.TempSwing = true
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case r.HighHumidity && r.TempSwing:
|
||||
r.Summary = "发病前环境存在持续高湿与温度突变,符合真菌病与核型多角体病诱发条件"
|
||||
case r.HighHumidity:
|
||||
r.Summary = fmt.Sprintf("发病前环境湿度 ≥%.0f%%,符合高湿诱病条件", humidityThreshold)
|
||||
case r.TempSwing:
|
||||
r.Summary = "发病前环境温度突变(>30℃ 或 <20℃),易诱发潜伏感染转急性"
|
||||
default:
|
||||
r.Summary = "发病前环境温湿度未见明显异常"
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// PastEvent 历史发病事件
|
||||
type PastEvent struct {
|
||||
Disease string
|
||||
DaysAgo int
|
||||
}
|
||||
|
||||
// HistoryAssociation 历史发病关联:同房间/同病种既往 90 天内有发病 → 连发性
|
||||
func HistoryAssociation(past []PastEvent, disease string) bool {
|
||||
for _, e := range past {
|
||||
if e.Disease == disease && e.DaysAgo <= 90 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TransmissionInference 按病种推断传播途径(规格书 3.8.2)
|
||||
func TransmissionInference(disease string) (mode, source string) {
|
||||
switch disease {
|
||||
case "核型多角体病":
|
||||
return "食下传染为主,创伤传染次之", "多角体污染蚕座/桑叶/蚕具,环境残留或新引入"
|
||||
case "白僵病", "其他真菌病(绿僵病/黄僵病/曲霉病)":
|
||||
return "接触传染/气流扩散", "分生孢子经气流带入,或蚕房尸体产孢内源扩散"
|
||||
case "软化病", "浓核病/传染性软化病":
|
||||
return "食下传染", "桑园害虫粪便污染桑叶,或蚕座环境累积污染"
|
||||
case "微粒子病":
|
||||
return "食下传染+胚种传染", "蚕种带毒(垂直传播)或环境孢子水平传播"
|
||||
case "细菌性败血病":
|
||||
return "创伤传染", "蚕具刮伤/互相抓伤后环境细菌侵入"
|
||||
case "猝倒病":
|
||||
return "食下传染(细菌毒素)", "桑叶被细菌污染"
|
||||
default:
|
||||
return "未知", "病种不明,建议先确认病原"
|
||||
}
|
||||
}
|
||||
|
||||
// ChecklistItem 排查清单项
|
||||
type ChecklistItem struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
InternalBias bool `json:"internalBias"`
|
||||
}
|
||||
|
||||
// DiseaseChecklist 分病种二级排查清单(规格书 3.8.3)
|
||||
func DiseaseChecklist(disease string) []ChecklistItem {
|
||||
switch disease {
|
||||
case "核型多角体病":
|
||||
return []ChecklistItem{
|
||||
{Key: "disinfect", Label: "上批次是否发生过同种病?消毒是否彻底(含蚕具浸泡)?", InternalBias: true},
|
||||
{Key: "tray_share", Label: "蚕具是否跨批次共用?", InternalBias: true},
|
||||
{Key: "mulberry", Label: "桑叶来源是否变更?", InternalBias: false},
|
||||
{Key: "entry", Label: "近期人员进出/蚕具借用记录是否有异常?", InternalBias: false},
|
||||
}
|
||||
case "白僵病":
|
||||
return []ChecklistItem{
|
||||
{Key: "vent", Label: "近期是否连续阴雨且通风不足?", InternalBias: true},
|
||||
{Key: "corpse", Label: "蚕房附近是否堆放蚕沙或病蚕尸体?", InternalBias: true},
|
||||
{Key: "outdoor", Label: "蚕房周边桑园昆虫是否有白僵病死亡个体?", InternalBias: false},
|
||||
{Key: "filter", Label: "通风口是否有过滤防虫措施?", InternalBias: false},
|
||||
}
|
||||
case "微粒子病":
|
||||
return []ChecklistItem{
|
||||
{Key: "seed", Label: "蚕种来源批次与供应商是否可追溯?", InternalBias: true},
|
||||
{Key: "quarantine", Label: "检疫证明与母蛾镜检结果是否齐全?", InternalBias: true},
|
||||
{Key: "wild", Label: "桑园周边野外昆虫是否有微孢子虫?", InternalBias: false},
|
||||
{Key: "cross", Label: "同批次蚕种在其他蚕房是否发病?", InternalBias: false},
|
||||
}
|
||||
case "软化病", "浓核病/传染性软化病":
|
||||
return []ChecklistItem{
|
||||
{Key: "density", Label: "蚕头密度是否过大?", InternalBias: true},
|
||||
{Key: "vent", Label: "通风是否不良?", InternalBias: true},
|
||||
{Key: "mulberry", Label: "桑叶是否潮湿或储运不达标?", InternalBias: true},
|
||||
{Key: "pest", Label: "桑园近期害虫防治记录是否正常?", InternalBias: false},
|
||||
}
|
||||
case "细菌性败血病":
|
||||
return []ChecklistItem{
|
||||
{Key: "tool", Label: "蚕具是否有尖锐边缘/毛刺?", InternalBias: true},
|
||||
{Key: "density", Label: "蚕头密度是否超标导致相互抓伤?", InternalBias: true},
|
||||
{Key: "op", Label: "给桑操作是否粗暴?", InternalBias: true},
|
||||
{Key: "disinfect", Label: "消毒是否有盲区?", InternalBias: true},
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ChecklistAnswer 清单作答
|
||||
type ChecklistAnswer struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Answer string `json:"answer"` // yes/no/unknown
|
||||
InternalBias bool `json:"internalBias"`
|
||||
}
|
||||
|
||||
// AnalyzeChecklist 按作答统计内源/外源倾向并给出结论
|
||||
func AnalyzeChecklist(answers []ChecklistAnswer) (origin string, confidence float64, conclusion string) {
|
||||
internal, external, unknown := 0, 0, 0
|
||||
for _, a := range answers {
|
||||
switch a.Answer {
|
||||
case "yes":
|
||||
if a.InternalBias {
|
||||
internal++
|
||||
} else {
|
||||
external++
|
||||
}
|
||||
case "no":
|
||||
if a.InternalBias {
|
||||
external++
|
||||
} else {
|
||||
internal++
|
||||
}
|
||||
default:
|
||||
unknown++
|
||||
}
|
||||
}
|
||||
total := internal + external + unknown
|
||||
if total == 0 {
|
||||
return "unknown", 0, "未填写排查清单,无法判定来源"
|
||||
}
|
||||
switch {
|
||||
case internal > external:
|
||||
origin = "internal"
|
||||
confidence = 0.5 + 0.3*float64(internal)/float64(total)
|
||||
conclusion = "倾向内源扩散(环境残留/蚕座污染),建议定向清除环境残留并升级消毒"
|
||||
case external > internal:
|
||||
origin = "external"
|
||||
confidence = 0.5 + 0.3*float64(external)/float64(total)
|
||||
conclusion = "倾向外源侵入(桑叶/气流/种源),建议封堵外部侵入途径"
|
||||
default:
|
||||
origin = "unknown"
|
||||
confidence = 0.5
|
||||
conclusion = "内源与外源线索相当,建议结合专家会诊进一步确认"
|
||||
}
|
||||
return origin, confidence, conclusion
|
||||
}
|
||||
Reference in New Issue
Block a user