8b1b4cde02
## 三个问题,一起解决 你问「模版要不要做成可选」,摸下来发现底下有三个更硬的: **① 根本没有多套模版可选。** 模板表唯一索引是 (species, stage) —— 同一个「猫 × 幼年期」只能有一套。「让用户选模版」在数据模型上做不到。 去掉唯一索引,加 Name / Summary / Sort / Enabled。 **② 今日任务和计划节点是两套,而且是我上一轮改出来的。** DailyTask 模板 kind=task 生成,ensureDayTasks 每天把昨天那份复制到今天 PlanTask 模板 kind=plan 生成,Day 偏移 计划改成月度管理 + 加了「应用到每天」之后,两者做的是同一件事, 但首页读 DailyTask、计划页管 PlanTask —— 用户在计划页加一条「每天喂药」, 首页今日任务里看不到。 DailyTask 退役。task.go 重写成 PlanTask 的一层「按日期取」的门面, /pets/:id/tasks 路径不变,小程序端不用为这次合并改调用。 PlanTask 加 Priority(首页那份要能标出哪条重要)。 顺手删掉重复的 CreateTask/UpdateTask/DeleteTask —— /plan-tasks 那套已经 覆盖了,留着两套会出现「首页管理弹层加的任务」和「计划页加的节点」进不同的表。 **③ 模板的 Day 偏移和月度计划对不上。** 月底建档的话 day=14/21/30 全落到 下个月,用户建完档打开计划页发现本月是空的。 改成重复规则:once(偏移 N 天,疫苗序列要用)/ daily / weekly(星期几) / monthly(几号)。套用 = 展开成目标月份的具体日期。 「每月 31 号」在 2 月按月末算,不然那个月直接没有。 列名用 repeat_rule 不用 repeat —— repeat 是 MySQL 保留字(和 group_key 同理)。 ## 展开成实体行,不是「存规则读时展开」 勾选状态是按天的。存规则得再开一张表记「哪天打过勾」,而且用户把 「每周二梳毛」这周挪到周三就要引入例外记录。一次展开成行,增删改和打勾全免费。 代价是套用后模板和计划断开:后台改模板不影响已套用的宠物。这是有意的—— 用户已经在自己的计划上勾了几十个勾,后台一改就被覆盖才是灾难。 ## 建档不再自动铺 seedPetDefaults 只建必要提醒(疫苗、驱虫)。漏针是真会出事的,而且它们是 「到期提醒」不占计划页的格子。其余交给用户挑方案。 自动铺的问题是:用户还没看懂这个 App 是干什么的,首页已经躺着十几条他没 答应过的任务,第一反应是「这些是什么」而不是「我该做什么」。 **改完发现还有第二条自动铺的路径**:newStagePlan 在建计划壳子时顺手把模板 铺成节点。所以我改完 seedPetDefaults 之后,建档后首页仍然躺着 5 条任务。 惰性生成尤其难查——不是建档时发生的,是第一次读计划才发生的。 ## 方案摘要按重复方式分开报数 一开始只报一个总数,「10 条」套用一整月展开成 130 条,用户会以为出 bug。 现在分成 每天 4 · 每周 0 · 每月 0 · 一次性 6 · 提醒 4。 ## 数据迁移(预生产库,已执行) 模板明细 kind=task → plan + repeat=daily 28 条 模板明细 kind=plan 补 repeat=once 44 条 8 套方案补名字和摘要 DailyTask → PlanTask 34 条,跳过 0 DailyTask 表的行**没删**,先留着当备份。 迁移第一版又踩了「列还没 AutoMigrate 出来就跑」那个坑:Count 静默返回 0, 我以为「没有需要补的」。第二版加了 HasColumn 前置检查才发现 44 条没转。 和上次 record_types.fields 回填是同一个错。 ## 验证(预生产库实跑) 今日任务/计划页今天 4 条 vs 4 条,一致 建档后 0 任务 / 0 计划节点 / 4 条必要提醒 套用本月 从今天到月末,4 条(7/30-7/31 两天) 重复套用 0 条新增(按「日期+标题」去重) 套用下月 130 条覆盖 31 天,每天 4.2 条 全部完成 5 → 0 未做 ## 还没做(下一步) 前端:建档第 4 步选方案、计划页空月份的套用入口、首页「管理」改跳计划页、 弹层删 manageTasks;后台模板页要支持多套 + 重复规则编辑。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
78 lines
3.9 KiB
Go
78 lines
3.9 KiB
Go
package model
|
||
|
||
// 养护方案:一套「这个物种、这个阶段该做哪些事」的配置。
|
||
// 后台可以给同一个 (species, stage) 配多套,建档时让用户挑一套套用,
|
||
// 也可以不套用、自己排。原来是一个组合只能有一套(唯一索引卡着),
|
||
// 「让用户选模版」这件事在数据模型上做不到。
|
||
|
||
// 物种取值
|
||
const (
|
||
SpeciesCat = "cat"
|
||
SpeciesDog = "dog"
|
||
)
|
||
|
||
// 明细的类别。原来还有个 task(今日任务),已经和 plan 合并——
|
||
// 「今天要做的事」就是「今天那一天的计划节点」,两套并存的结果是
|
||
// 用户在计划页加的「每天喂药」在首页看不到。
|
||
const (
|
||
CareKindPlan = "plan" // 计划节点(含每天/每周/每月重复)
|
||
CareKindReminder = "reminder" // 到期提醒(疫苗、驱虫这类有固定周期的)
|
||
)
|
||
|
||
// 重复规则。月度计划要的是「多久做一次」,不是「第几天做」——
|
||
// 原来只有 Day 偏移,月底建档的话 day=14/21/30 全落到下个月,
|
||
// 用户建完档打开计划页发现本月是空的。
|
||
const (
|
||
RepeatOnce = "once" // 一次性,按 Day 偏移(到家第 N 天,疫苗序列要用)
|
||
RepeatDaily = "daily" // 每天
|
||
RepeatWeekly = "weekly" // 每周某几天,看 Weekdays
|
||
RepeatMonthly = "monthly" // 每月某天,看 MonthDay
|
||
)
|
||
|
||
// CareTemplate 一套方案
|
||
type CareTemplate struct {
|
||
Base
|
||
Species string `gorm:"size:16;index" json:"species"` // cat | dog
|
||
Stage string `gorm:"size:48;index" json:"stage"` // 与 onboarding 阶段一致
|
||
// Name 方案名。建档时用户看到的就是这个,要能一眼分辨
|
||
// (「幼猫标准照护」/「幼猫省心版」)
|
||
Name string `gorm:"size:64" json:"name"`
|
||
// Summary 一句话摘要,选择列表里展示
|
||
Summary string `gorm:"size:255" json:"summary"`
|
||
Sort int `json:"sort"`
|
||
Enabled bool `gorm:"default:true" json:"enabled"`
|
||
|
||
Items []CareTemplateItem `gorm:"foreignKey:TemplateID" json:"items"`
|
||
}
|
||
|
||
// CareTemplateItem 明细,kind 区分两类;不同类只用到部分字段
|
||
type CareTemplateItem struct {
|
||
Base
|
||
TemplateID string `gorm:"size:24;index" json:"template_id"`
|
||
Kind string `gorm:"size:16;index" json:"kind"` // plan | reminder
|
||
Sort int `json:"sort"`
|
||
Title string `gorm:"size:128" json:"title"`
|
||
Description string `gorm:"size:512" json:"description"`
|
||
|
||
// plan 专用
|
||
Priority string `gorm:"size:16" json:"priority"` // 重要 / 普通(空)
|
||
SheetType string `gorm:"size:32" json:"sheet_type"` // 关联记录类型 code:weight/poop/bath...
|
||
// Repeat 列名用 repeat_rule:repeat 是 MySQL 保留字,GORM 虽然会加反引号,
|
||
// 但手写 SQL 排查时很容易踩(和 group_key 同样的理由)
|
||
Repeat string `gorm:"column:repeat_rule;size:16" json:"repeat"` // once | daily | weekly | monthly
|
||
Day int `json:"day"` // once:从套用日起第 N 天(0=当天)
|
||
// DayLabel 留着:后台的 AI 生成模板那条链路在产出它。月度计划里日期是绝对的
|
||
// 不需要「第 N 天」标签,但删它要连着改 AI 解析,不在这次范围内
|
||
DayLabel string `gorm:"size:32" json:"day_label"`
|
||
// Weekdays weekly 用:逗号分隔的 1-7(1=周一)。
|
||
// 存字符串不另开表——一条模板明细最多 7 个数字,开表换来的只是查询灵活性,
|
||
// 而这份数据从来只整条读出来展开
|
||
Weekdays string `gorm:"size:16" json:"weekdays"`
|
||
MonthDay int `json:"month_day"` // monthly 用:几号(1-31),超出当月天数按月末算
|
||
|
||
// reminder 专用
|
||
ReminderType string `gorm:"size:32" json:"reminder_type"` // vaccine/deworm/weight/monthlyReport
|
||
OffsetDays int `json:"offset_days"` // 到期 = 今天 + N 天;<=0 表示无固定到期,用频率
|
||
Frequency string `gorm:"size:64" json:"frequency"` // 频率描述,如「每周二、周五」
|
||
}
|