Files
sundynix-micro-be/app/plant/rpc/internal/logic/completeTaskLogic.go
T
2026-05-24 01:41:22 +08:00

175 lines
5.8 KiB
Go

package logic
import (
"context"
"errors"
"time"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type CompleteTaskLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCompleteTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CompleteTaskLogic {
return &CompleteTaskLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
const taskReward = int64(50)
func (l *CompleteTaskLogic) CompleteTask(in *plant.CompleteTaskReq) (*plant.TaskCompletionResult, error) {
var result plant.TaskCompletionResult
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// 1. 查任务
var task plantModel.CareTask
if err := tx.Set("gorm:query_option", "FOR UPDATE").
Where("id = ? AND user_id = ?", in.TaskId, in.UserId).First(&task).Error; err != nil {
return err
}
if task.Status != 1 {
return errors.New("任务已完成或已过期,不能重复完成")
}
// 2. 标记完成
now := time.Now()
if err := tx.Model(&task).Updates(map[string]interface{}{
"status": 2, "completed_at": now,
}).Error; err != nil {
return err
}
// 3. 查计划,生成下个周期任务
var plan plantModel.CarePlan
if err := tx.Where("id = ?", task.PlanID).First(&plan).Error; err != nil {
return err
}
today := time.Now()
todayZero := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location())
nextDue := todayZero.AddDate(0, 0, plan.Period)
// 检查是否已经存在该计划的待办任务,避免重复生成相同事项的多个待办任务
var activeCount int64
if err := tx.Model(&plantModel.CareTask{}).
Where("plan_id = ? AND status = 1", plan.ID).
Count(&activeCount).Error; err != nil {
return err
}
if activeCount == 0 {
newTask := plantModel.CareTask{
UserID: plan.UserID, PlantID: plan.PlantID, PlanID: plan.ID,
Name: plan.Name, Icon: plan.Icon, TargetAction: plan.TargetAction,
DueDate: nextDue, Status: 1,
}
if err := tx.Create(&newTask).Error; err != nil {
return err
}
}
// 4. 保存养护记录
record := plantModel.CareRecord{
UserID: plan.UserID, PlantID: plan.PlantID, PlanID: plan.ID,
Name: plan.Name, Icon: plan.Icon, Remark: in.Remark,
}
if err := tx.Create(&record).Error; err != nil {
return err
}
// 5. 查用户 profile(加锁)
var profile plantModel.UserProfile
if err := tx.Set("gorm:query_option", "FOR UPDATE").
Where("user_id = ?", in.UserId).First(&profile).Error; err != nil {
return err
}
fieldMap := map[string]string{
"ACT_WATER": "water_count", "ACT_FERTILIZE": "fertilize_count",
"ACT_REPOT": "repot_count", "ACT_PRUNE": "prune_count",
}
col, ok := fieldMap[task.TargetAction]
if !ok {
col = "care_count"
}
newTotal := profile.TotalSunlight + taskReward
// 5.1 等级判定
var latestLevel plantModel.LevelConfig
levelErr := tx.Where("min_sunlight <= ?", newTotal).
Order("min_sunlight DESC").First(&latestLevel).Error
if levelErr != nil {
if errors.Is(levelErr, gorm.ErrRecordNotFound) {
// 没有等级配置,跳过等级相关逻辑
result.IsLevelUp = false
result.RewardSunlight = taskReward
// 仍要更新阳光值
profileData := map[string]interface{}{
col: gorm.Expr(col + " + 1"),
"current_sunlight": profile.CurrentSunlight + taskReward,
"total_sunlight": newTotal,
}
if err := tx.Model(&plantModel.UserProfile{}).
Where("user_id = ?", in.UserId).Updates(profileData).Error; err != nil {
return err
}
return nil // 跳过后续等级/徽章逻辑
}
return errors.New("等级配置异常")
}
result.IsLevelUp = (latestLevel.ID != profile.LevelID)
result.CurrentLevel = &plant.LevelConfigInfo{
Id: latestLevel.ID, Level: int32(latestLevel.Level),
Title: latestLevel.Title, MinSunlight: latestLevel.MinSunlight,
}
result.RewardSunlight = taskReward
// 5.2 更新 profile
profileData := map[string]interface{}{
col: gorm.Expr(col + " + 1"),
"current_sunlight": profile.CurrentSunlight + taskReward,
"total_sunlight": newTotal,
"level_id": latestLevel.ID,
}
if err := tx.Model(&plantModel.UserProfile{}).
Where("user_id = ?", in.UserId).Updates(profileData).Error; err != nil {
return err
}
// 6. 徽章判定
actionValMap := map[string]int64{
"ACT_WATER": profile.WaterCount + 1, "ACT_FERTILIZE": profile.FertilizeCount + 1,
"ACT_REPOT": profile.RepotCount + 1, "ACT_PRUNE": profile.PruneCount + 1,
}
currentVal := actionValMap[task.TargetAction]
var ownedIds []string
tx.Model(&plantModel.UserBadge{}).Where("user_id = ?", in.UserId).Pluck("badge_id", &ownedIds)
ownedMap := make(map[string]bool)
for _, id := range ownedIds {
ownedMap[id] = true
}
var badgeConfigs []plantModel.BadgeConfig
tx.Where("target_action = ?", task.TargetAction).Find(&badgeConfigs)
var bestBadge *plantModel.BadgeConfig
for i := range badgeConfigs {
bc := &badgeConfigs[i]
if !ownedMap[bc.ID] && currentVal >= bc.Threshold {
if bestBadge == nil || bc.Tier > bestBadge.Tier {
bestBadge = bc
}
}
}
if bestBadge != nil {
ub := plantModel.UserBadge{UserID: in.UserId, BadgeID: bestBadge.ID}
if err := tx.Create(&ub).Error; err != nil {
return err
}
tx.Model(&plantModel.UserProfile{}).Where("user_id = ?", in.UserId).
UpdateColumn("current_sunlight", gorm.Expr("current_sunlight + ?", bestBadge.RewardSunlight))
result.IsGetBadge = true
result.NewBadge = &plant.BadgeConfigInfo{
Id: bestBadge.ID, Name: bestBadge.Name, Dimension: bestBadge.Dimension,
Tier: int32(bestBadge.Tier), RewardSunlight: bestBadge.RewardSunlight,
}
}
return nil
})
return &result, err
}