feat: 每日任务顺延与增删改 + 计划到期续期 + 靠谱养护模板 + 图片URL修复

后端
- 每日任务惰性生成:某天为空则从最近一天顺延复制(done 重置),首天用模板
- 任务增删改 API(POST /pets/:id/tasks、PUT/DELETE /tasks/:id),改动自动顺延
- 30 天计划到期自动归档并按当前阶段生成新一轮
- 内置养护模板重写为兽医常识向:狗每阶段含遛狗/牵引/狂犬,猫含猫砂/梳毛/饮水
- 文件 URL 改为按 object_name + 当前配置动态拼接,换 IP/域名不再有旧地址

小程序
- 首页今日任务加「管理」入口:增删改任务弹层
- 记录时间轴显示照片缩略图(可全屏预览)+ 拍照成功提示
- 社区发布条精简为单个「发布图文」按钮,去掉头像/输入框误触

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-06 10:06:13 +08:00
parent 2f5dd6eefc
commit 28b95a6468
21 changed files with 478 additions and 81 deletions
+41 -3
View File
@@ -24,14 +24,52 @@ func (s *Service) TogglePlanTask(userID, taskID string) error {
return s.db.Model(&model.PlanTask{}).Where("id = ?", taskID).Update("done", !pt.Done).Error
}
// GetPlan 宠物 30 天计划(含明细
// newStagePlan 宠物当前阶段的养护模板生成一份 30 天计划(未保存
func (s *Service) newStagePlan(pet *model.Pet, start time.Time) model.Plan {
var planTasks []model.PlanTask
for _, it := range s.careTemplateItems(SpeciesOf(pet.Type), pet.Stage) {
if it.Kind == model.CareKindPlan {
planTasks = append(planTasks, model.PlanTask{
Day: it.Day, DayLabel: it.DayLabel, Title: it.Title, Description: it.Description, SheetType: it.SheetType,
})
}
}
end := start.AddDate(0, 0, 30)
return model.Plan{
PetID: pet.ID, UserID: pet.UserID, Kind: model.PlanThirtyDay, Stage: pet.Stage,
StartDate: &start, EndDate: &end, CompletionPct: 0, Status: "active", Tasks: planTasks,
}
}
// ensurePlan 计划到期自动续期:active 计划过期则归档并按当前阶段生成新一轮;无 active 则补建
func (s *Service) ensurePlan(pet *model.Pet) {
now := time.Now()
var plan model.Plan
err := s.db.Where("pet_id = ? AND kind = ? AND status = ?", pet.ID, model.PlanThirtyDay, "active").
Order("id desc").First(&plan).Error
if err == gorm.ErrRecordNotFound {
p := s.newStagePlan(pet, now)
s.db.Create(&p)
return
}
if err == nil && plan.EndDate != nil && plan.EndDate.Before(now) {
s.db.Model(&model.Plan{}).Where("id = ?", plan.ID).Update("status", "archived")
p := s.newStagePlan(pet, now)
s.db.Create(&p)
}
}
// GetPlan 取宠物的 30 天计划(含明细);过期自动续期
func (s *Service) GetPlan(userID, petID string) (*model.Plan, error) {
if _, err := s.ownedPet(userID, petID); err != nil {
pet, err := s.ownedPet(userID, petID)
if err != nil {
return nil, err
}
s.ensurePlan(pet)
var plan model.Plan
err := s.db.Preload("Tasks").
err = s.db.Preload("Tasks").
Where("pet_id = ? AND kind = ?", petID, model.PlanThirtyDay).
Where("status = ?", "active").
Order("id desc").First(&plan).Error
if err == gorm.ErrRecordNotFound {
return nil, ErrNotFound