package database import ( "gorm.io/gorm" "github.com/sundynix/pets-be/internal/model" ) // 养护方案预置:5 个阶段 × 2 物种,各一套「标准照护」。 // // 内容按各阶段的真实生理重点配,不是把同一份东西换个名字: // 幼年期 疫苗序列 + 驱虫 + 社会化,大型犬还要控制生长速度 // 青年期 绝育窗口 + 换成年粮 // 成年期 维持期,重点是把基线数据记下来(体重趋势、年度体检) // 中年期 年度血检开始有意义,体重和牙结石重点看 // 老年期 体检频率加倍,猫看肾和甲状腺、狗看关节和心脏 // // 和 record_types / breeds 一样只在表为空时铺一次,之后一切以数据库为准。 // // 这些是「结构」不是「内容」:方案表空着的话建档最后一步没东西可挑, // 用户建完档只能自己从零排计划。 func plan(title, desc, repeat, sheet string) model.CareTemplateItem { return model.CareTemplateItem{Kind: model.CareKindPlan, Repeat: repeat, Title: title, Description: desc, SheetType: sheet} } func weekly(title, desc, weekdays, sheet string) model.CareTemplateItem { it := plan(title, desc, model.RepeatWeekly, sheet) it.Weekdays = weekdays return it } func monthly(title, desc string, day int, sheet string) model.CareTemplateItem { it := plan(title, desc, model.RepeatMonthly, sheet) it.MonthDay = day return it } func once(title, desc string, day int, sheet string) model.CareTemplateItem { it := plan(title, desc, model.RepeatOnce, sheet) it.Day = day return it } func remind(rtype, title string, offset int, freq string) model.CareTemplateItem { return model.CareTemplateItem{Kind: model.CareKindReminder, ReminderType: rtype, Title: title, OffsetDays: offset, Frequency: freq} } func important(it model.CareTemplateItem) model.CareTemplateItem { it.Priority = "重要" return it } // TplDef 一套方案的定义(迁移程序要读它) type tplDef struct { Species string Stage string Name string Summary string Items []model.CareTemplateItem } // CareTemplateDefs 导出给一次性迁移程序用 func CareTemplateDefs() []tplDef { return careTemplateDefs() } // SeedCareTemplatesForce 不检查表空,直接铺。只给迁移程序用 func SeedCareTemplatesForce(db *gorm.DB) error { return insertCareTemplates(db) } func careTemplateDefs() []tplDef { const ( J = "幼年期" Y = "青年期" A = "成年期" M = "中年期" S = "老年期" ) return []tplDef{ // ───────────────── 猫 ───────────────── {model.SpeciesCat, J, "幼猫标准照护", "少量多餐、疫苗序列和社会化,六月龄前把基础打好", []model.CareTemplateItem{ important(plan("喂食 3-4 次", "幼猫胃容量小,一天分 3-4 顿,用幼猫粮", model.RepeatDaily, "food")), plan("清理猫砂盆", "每天铲一次,顺手看便便形状和颜色", model.RepeatDaily, "litterbox"), plan("陪玩 15 分钟", "逗猫棒模拟狩猎,消耗精力也是社会化", model.RepeatDaily, ""), weekly("称一次体重", "幼猫每周都在长,体重曲线比单次数值有用", "1", "weight"), weekly("检查耳朵和眼睛", "幼猫耳螨和眼部分泌物高发", "4", "ear"), once("核对疫苗进度", "对照月龄看第几针,三针核心疫苗要在六月龄前打完", 2, "vaccine"), once("首次体内驱虫", "幼猫驱虫比成猫频繁,按体重给药", 5, "deworm"), remind("vaccine", "下一针疫苗", 21, "幼猫期每 3-4 周一针,共三针"), remind("deworm", "体内外驱虫", 30, "幼猫每月一次"), remind("weight", "记录体重", 7, "每周一次"), }}, {model.SpeciesCat, Y, "青年猫标准照护", "绝育窗口、换成猫粮,开始把体重控住", []model.CareTemplateItem{ plan("喂食 2 次", "六月龄后可以改成早晚两顿", model.RepeatDaily, "food"), plan("清理猫砂盆", "每天铲一次", model.RepeatDaily, "litterbox"), plan("陪玩 15 分钟", "性成熟后精力旺盛,玩够了才不拆家", model.RepeatDaily, ""), weekly("称一次体重", "绝育前后代谢会变,体重要盯紧", "1", "weight"), weekly("梳毛", "换毛期减少毛球", "4", "brush"), once("咨询绝育时间", "多数猫 6-8 月龄可做,术前要空腹和体检", 3, "vet"), once("换成猫粮", "一岁前后用 7 天时间按比例过渡,别一次换完", 14, "food"), remind("vaccine", "年度加强疫苗", 60, "一年一次"), remind("deworm", "体内外驱虫", 60, "每 2-3 个月一次"), }}, {model.SpeciesCat, A, "成年猫标准照护", "维持期,重点是把基线数据记下来", []model.CareTemplateItem{ plan("喂食和换水", "定时定量,水每天换", model.RepeatDaily, "food"), plan("清理猫砂盆", "每天铲一次,便便异常是最早的信号", model.RepeatDaily, "litterbox"), weekly("梳毛", "长毛猫每天,短毛猫每周两次", "2,5", "brush"), weekly("刷牙", "牙结石是成年猫最常被忽略的问题", "6", "tooth"), monthly("称一次体重", "成年后体重该是平的,涨跌超过 5% 要留意", 1, "weight"), monthly("洗猫砂盆", "整盆倒空清洗一次", 15, "litterbox"), once("安排年度体检", "成年期一年一次,做基础血检建立基线", 7, "checkup"), remind("vaccine", "年度加强疫苗", 90, "一年一次"), remind("deworm", "体内外驱虫", 90, "每 3 个月一次"), remind("monthlyReport", "看看这个月的记录", 30, "每月一次"), }}, {model.SpeciesCat, M, "中年猫标准照护", "七岁起代谢变慢,年度血检开始能查出早期问题", []model.CareTemplateItem{ plan("喂食和换水", "中年后容易发胖,按体重控制热量", model.RepeatDaily, "food"), plan("清理猫砂盆", "尿量和尿块大小要留意,肾病最早从这里看出来", model.RepeatDaily, "litterbox"), weekly("梳毛", "自我梳理会减少,需要帮忙", "2,5", "brush"), weekly("刷牙", "牙结石到这个年龄已经开始影响进食", "6", "tooth"), monthly("称一次体重", "不明原因掉体重是中年猫要警惕的信号", 1, "weight"), monthly("检查口腔", "看牙龈颜色和有没有结石", 15, ""), once("安排年度体检", "血检 + 尿检,重点看肾功能和甲状腺", 7, "checkup"), remind("vaccine", "年度加强疫苗", 90, "一年一次"), remind("deworm", "体内外驱虫", 90, "每 3 个月一次"), remind("weight", "记录体重", 30, "每月一次"), }}, {model.SpeciesCat, S, "老年猫标准照护", "体检半年一次,肾和甲状腺是重点", []model.CareTemplateItem{ important(plan("记录饮水量", "多饮多尿是慢性肾病最典型的早期表现", model.RepeatDaily, "water")), plan("喂食", "老年猫可能需要处方粮,少量多次更好消化", model.RepeatDaily, "food"), plan("清理猫砂盆", "尿块变大或变多要立刻记下来", model.RepeatDaily, "litterbox"), weekly("梳毛", "老年猫自己舔不到的地方要帮它梳", "2,5", "brush"), weekly("称一次体重", "老年期体重变化要按周盯,不是按月", "1", "weight"), monthly("检查关节活动", "跳不上原来能跳的地方就是信号", 1, ""), once("安排半年体检", "血检 + 尿检 + 甲状腺,老年期半年一次", 7, "checkup"), remind("vaccine", "年度加强疫苗", 90, "一年一次(可与医生商量减免)"), remind("deworm", "体内外驱虫", 90, "每 3 个月一次"), remind("monthlyReport", "看看这个月的记录", 30, "每月一次"), }}, // ───────────────── 狗 ───────────────── {model.SpeciesDog, J, "幼犬标准照护", "疫苗序列、社会化,大型犬还要控制生长速度", []model.CareTemplateItem{ important(plan("喂食 3-4 次", "幼犬一天分 3-4 顿;大型犬别过量补钙,长太快伤关节", model.RepeatDaily, "food")), plan("外出排便和散步", "疫苗打齐前避免去流浪动物多的地方", model.RepeatDaily, ""), plan("基础训练 10 分钟", "坐、等待、唤回。幼犬期是社会化窗口,错过很难补", model.RepeatDaily, ""), weekly("称一次体重", "幼犬长得快,体重曲线也是喂食量的依据", "1", "weight"), weekly("检查爪垫和耳朵", "散步后看有没有扎伤,垂耳犬要重点看耳道", "4", "ear"), once("核对疫苗进度", "对照月龄看第几针,狂犬疫苗按当地规定单独安排", 2, "vaccine"), once("首次体内驱虫", "按体重给药,幼犬比成犬频繁", 5, "deworm"), remind("vaccine", "下一针疫苗", 21, "幼犬期每 3-4 周一针"), remind("deworm", "体内外驱虫", 30, "幼犬每月一次"), remind("weight", "记录体重", 7, "每周一次"), }}, {model.SpeciesDog, Y, "青年犬标准照护", "骨骼刚闭合,可以加运动量了;绝育窗口也在这里", []model.CareTemplateItem{ plan("喂食 2 次", "改成早晚两顿", model.RepeatDaily, "food"), plan("散步 30 分钟以上", "骨骼闭合后可以正常运动,之前不宜长距离跑跳", model.RepeatDaily, ""), plan("训练 10 分钟", "把幼犬期学的固化下来", model.RepeatDaily, ""), weekly("称一次体重", "换成犬粮期间容易长胖", "1", "weight"), weekly("梳毛", "换毛期每天,平时每周两次", "2,5", "brush"), once("咨询绝育时间", "大型犬建议等骨骼闭合后再做", 3, "vet"), once("换成犬粮", "用 7 天按比例过渡,别一次换完", 14, "food"), remind("vaccine", "年度加强疫苗", 60, "一年一次"), remind("deworm", "体内外驱虫", 60, "每 2-3 个月一次"), }}, {model.SpeciesDog, A, "成年犬标准照护", "维持期,重点是把基线数据记下来", []model.CareTemplateItem{ plan("喂食和换水", "定时定量,水每天换", model.RepeatDaily, "food"), plan("散步 30-60 分钟", "运动量不够是成年犬发胖和行为问题的主因", model.RepeatDaily, ""), weekly("梳毛", "顺手摸一遍有没有肿块", "2,5", "brush"), weekly("刷牙", "牙结石是成年犬最常被忽略的问题", "6", "tooth"), monthly("称一次体重", "成年后体重该是平的,涨跌超过 5% 要留意", 1, "weight"), monthly("剪指甲", "听到走路哒哒响就该剪了", 15, "nail"), once("安排年度体检", "一年一次,做基础血检建立基线", 7, "checkup"), remind("vaccine", "年度加强疫苗", 90, "一年一次"), remind("deworm", "体内外驱虫", 90, "每 3 个月一次"), remind("monthlyReport", "看看这个月的记录", 30, "每月一次"), }}, {model.SpeciesDog, M, "中年犬标准照护", "体重和关节是重点,年度血检开始有意义", []model.CareTemplateItem{ plan("喂食和换水", "代谢变慢,按体重下调热量", model.RepeatDaily, "food"), plan("散步 30 分钟", "保持规律但降低强度,避免急停急转", model.RepeatDaily, ""), weekly("梳毛并摸一遍身体", "中年后肿块发生率上升,每周摸一遍是最便宜的筛查", "2,5", "brush"), weekly("刷牙", "牙结石到这个年龄已经开始影响进食", "6", "tooth"), monthly("称一次体重", "中年犬超重会直接加重关节负担", 1, "weight"), monthly("检查关节活动", "起身变慢、不愿上楼就是信号", 15, ""), once("安排年度体检", "血检 + 关节检查,大型犬加拍髋关节", 7, "checkup"), remind("vaccine", "年度加强疫苗", 90, "一年一次"), remind("deworm", "体内外驱虫", 90, "每 3 个月一次"), remind("weight", "记录体重", 30, "每月一次"), }}, {model.SpeciesDog, S, "老年犬标准照护", "体检半年一次,关节和心脏是重点", []model.CareTemplateItem{ important(plan("记录精神和食欲", "老年犬变化来得快,每天一句话记下来", model.RepeatDaily, "")), plan("喂食", "老年犬可能需要处方粮,少量多次更好消化", model.RepeatDaily, "food"), plan("散步 15-20 分钟", "短而多次,比一次走久对关节友好", model.RepeatDaily, ""), weekly("梳毛并摸一遍身体", "重点摸有没有新长的肿块", "2,5", "brush"), weekly("称一次体重", "老年期体重变化要按周盯", "1", "weight"), monthly("检查关节和牙齿", "起身、上楼、咀嚼有没有变吃力", 1, ""), once("安排半年体检", "血检 + 心脏听诊 + 关节,老年期半年一次", 7, "checkup"), remind("vaccine", "年度加强疫苗", 90, "一年一次(可与医生商量减免)"), remind("deworm", "体内外驱虫", 90, "每 3 个月一次"), remind("monthlyReport", "看看这个月的记录", 30, "每月一次"), }}, } } // seedCareTemplates 只在方案表为空时铺一次 func seedCareTemplates(db *gorm.DB) error { var count int64 if err := db.Model(&model.CareTemplate{}).Count(&count).Error; err != nil { return err } if count > 0 { return nil } return insertCareTemplates(db) } func insertCareTemplates(db *gorm.DB) error { for i, d := range careTemplateDefs() { tpl := model.CareTemplate{ Species: d.Species, Stage: d.Stage, Name: d.Name, Summary: d.Summary, Sort: (i + 1) * 10, Enabled: true, } if err := db.Create(&tpl).Error; err != nil { return err } items := make([]model.CareTemplateItem, 0, len(d.Items)) for j, it := range d.Items { it.TemplateID = tpl.ID it.Sort = j items = append(items, it) } if len(items) > 0 { if err := db.Create(&items).Error; err != nil { return err } } } return nil }