package database import ( "golang.org/x/crypto/bcrypt" "gorm.io/gorm" "github.com/sundynix/pets-be/internal/config" "github.com/sundynix/pets-be/internal/model" ) // Seed 初始化种子数据:管理员账号 + 新手文章 func Seed(db *gorm.DB, cfg *config.Config) error { if err := seedAdmin(db, cfg); err != nil { return err } if err := seedArticles(db); err != nil { return err } // 早期版本 seed 出来的文章没有正文,这里补上(不覆盖已在后台编辑过的) return backfillArticleContent(db) } func seedAdmin(db *gorm.DB, cfg *config.Config) error { var count int64 if err := db.Model(&model.Admin{}).Where("username = ?", cfg.Admin.Username).Count(&count).Error; err != nil { return err } if count > 0 { return nil } hash, err := bcrypt.GenerateFromPassword([]byte(cfg.Admin.Password), bcrypt.DefaultCost) if err != nil { return err } return db.Create(&model.Admin{ Username: cfg.Admin.Username, PasswordHash: string(hash), Role: "admin", }).Error } // 按标题补齐:库里没有的才插入。这样以后往内置清单里加文章, // 老库启动时也能自动拿到新的,而管理员改过或删掉的不会被复活…… // 删掉的会被复活是这个做法的代价,所以只在标题完全不存在时插。 func seedArticles(db *gorm.DB) error { all := append(builtinArticles(), moreArticles()...) var titles []string if err := db.Model(&model.Article{}).Pluck("title", &titles).Error; err != nil { return err } exists := make(map[string]bool, len(titles)) for _, t := range titles { exists[t] = true } var missing []model.Article for _, a := range all { if !exists[a.Title] { missing = append(missing, a) } } if len(missing) == 0 { return nil } return db.Create(&missing).Error } // backfillArticleContent 给早期 seed 出来的空正文文章补内容(按标题匹配,只补空的,不覆盖已编辑过的) func backfillArticleContent(db *gorm.DB) error { for _, a := range builtinArticles() { if err := db.Model(&model.Article{}). Where("title = ? AND (content IS NULL OR content = '')", a.Title). Updates(map[string]any{"content": a.Content, "description": a.Description}).Error; err != nil { return err } } return nil } // builtinArticles 内置新手文章。内容遵循与 AI 助手一致的安全护栏: // 不做诊断、不给药物剂量,异常一律说明何时就医。 func builtinArticles() []model.Article { return []model.Article{ { Icon: "💉", Title: "幼猫疫苗和驱虫怎么安排?", Description: "疫苗针次、驱虫间隔和常见误区,看完顺手设个提醒。", Category: "vaccine", RelatedSheetType: "vaccine", Published: true, Content: `刚接回家的猫,疫苗和驱虫是最容易漏掉、也最不能漏的两件事。 【核心疫苗:猫三联】 · 一般从 8 周龄左右开始,共打 3 针,每针间隔 3-4 周。 · 之后每年加强 1 针。 · 狂犬疫苗按当地规定接种,多数地区建议满 3 月龄后接种。 【接种前后注意】 · 只有健康的猫才能打疫苗。有腹泻、打喷嚏、精神差时先缓一缓。 · 接种前后一周不要洗澡、不要换粮、不要长途出行,减少应激。 · 打完在医院观察 20-30 分钟,回家后再留意 24 小时。 · 少数猫会精神略差或轻微发热,通常一两天缓解。 【驱虫】 · 体内驱虫:幼猫期约每月 1 次,成年后每 3 个月 1 次。 · 体外驱虫:出门或家里有狗时更要做,按产品说明周期。 · 完全室内、不接触其他动物的成年猫可适当拉长间隔,但不要长期不做。 【常见误区】 · 「猫不出门就不用打疫苗」——你的鞋底、衣物都可能带病原回家。 · 「三针打完就一劳永逸」——每年仍需加强。 · 「驱虫药随便买」——务必按体重选择,剂量以医生或说明书为准。 【什么时候要去医院】 接种后出现持续呕吐、面部肿胀、呼吸急促、精神明显萎靡,请立即就医。 小程序里可以直接把下一针疫苗和驱虫时间设成提醒,到期会通知你。`, }, { Icon: "💩", Title: "猫咪软便要不要去医院?", Description: "先分清程度,学会记录观察,明确什么情况必须就医。", Category: "symptom", RelatedSheetType: "symptom", Published: true, Content: `软便很常见,但「常见」不等于「不用管」。关键是分清程度和持续时间。 【先看程度】 · 成形但偏软、能捡起来:多数是饮食或轻微应激,可以先观察。 · 糊状、不成形:需要留意,记录次数和颜色。 · 水样、喷射状:脱水风险高,别拖。 【常见诱因】 · 换粮太快(最常见):新旧粮要 7 天以上过渡。 · 应激:刚到家、搬家、家里来客人。 · 吃多了、吃了人食或零食过量。 · 寄生虫:尤其是没做过驱虫的幼猫。 · 着凉、毛球、食物不耐受。 【在家可以做的】 · 记录:什么时候开始、一天几次、颜色形状、有没有带血或黏液。 · 暂停新食物和零食,回到之前吃惯的粮。 · 保证饮水,注意保暖。 · 不要自行喂人用止泻药。 【出现这些情况,尽快就医】 · 持续超过 24 小时没有好转; · 便中带血、发黑,或有大量黏液; · 伴随呕吐、不吃不喝、精神明显变差; · 幼猫、老年猫或本身有基础病的猫——它们脱水很快,标准要更严。 【就医时带上什么】 排便照片、开始时间、次数、近期饮食变化、驱虫和疫苗记录。小程序里的「异常观察」可以直接生成一份就医前摘要。 本文只提供观察思路,不能替代医生诊断。`, }, { Icon: "🍽️", Title: "7 天换粮计划怎么做?", Description: "按比例过渡,配合排便记录,把换粮的肠胃风险降到最低。", Category: "food", RelatedSheetType: "applyPlan", Published: true, Content: `直接换粮是软便的头号原因。正确做法是按比例、分阶段过渡。 【标准 7 天方案】 · 第 1-2 天:新粮 25% + 旧粮 75% · 第 3-4 天:新粮 50% + 旧粮 50% · 第 5-6 天:新粮 75% + 旧粮 25% · 第 7 天起:新粮 100% 肠胃敏感、幼猫或老年猫,把周期拉长到 10-14 天更稳。 【过渡期要盯什么】 · 每天记录排便状态:成形 / 软便 / 拉稀。 · 食欲有没有下降,有没有呕吐。 · 一旦出现软便:退回上一个比例,稳定 2-3 天再往前推,别硬推。 【几个细节】 · 一次只换一种变量。别同时换粮又换猫砂、又带去洗澡。 · 新旧粮要拌匀,不然它会挑出爱吃的。 · 换粮期间别加新零食,否则出问题分不清是谁的锅。 · 湿粮开封后冷藏并尽快吃完,别放到下一顿还常温摆着。 【什么时候暂停换粮去看医生】 拉稀持续超过 24 小时、便中带血、频繁呕吐或精神变差,先就医,别继续过渡。 在小程序里每天记一次「饮食」和「便便」,7 天后能直接看到这次换粮顺不顺。`, }, { Icon: "💰", Title: "一个月养猫大概花多少钱?", Description: "拆开算固定支出和一次性支出,重点是留出医疗备用金。", Category: "cost", RelatedSheetType: "cost", Published: true, Content: `养猫的花销差异很大,但结构是固定的。看清结构再决定预算。 【每月固定支出】 · 猫粮:口粮占大头,按体重和粮的档次浮动。 · 猫砂:用量比想象中大,膨润土和豆腐砂消耗速度不同。 · 零食 / 营养品:可选,不是必需。 【周期性支出】 · 驱虫:体内体外,按周期。 · 疫苗:幼猫第一年针次多,之后每年一次。 · 体检:成年猫每年 1 次,老年猫建议每半年 1 次。 【一次性支出(第一年)】 猫砂盆、猫窝、food/水碗、猫爬架、航空箱、指甲剪、梳子;绝育是第一年最大的一笔。 【最容易被低估的一项:医疗】 真正让预算失控的从来不是猫粮,是突发医疗。常见的口炎、尿闭、异物、肾病,一次就可能超过全年口粮开销。 建议:单独留一笔医疗备用金,或了解一下宠物医疗保险。这比省几十块钱的粮重要得多。 【怎么省得聪明】 · 该省:花哨玩具、频繁换的网红零食。 · 不该省:口粮质量、驱虫疫苗、该做的体检——省这些通常是把钱挪到医疗账单上。 在小程序里每次消费顺手记一笔,月底就能看到自己的真实结构,比拍脑袋估算准得多。`, }, } }