package mysqldao import ( "context" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/global" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqlmodel" "gorm.io/gorm" ) // BenefitGrantTaskDao 权益发放补偿任务数据访问 type BenefitGrantTaskDao struct { db *gorm.DB } // NewBenefitGrantTaskDao 创建补偿任务 DAO func NewBenefitGrantTaskDao() *BenefitGrantTaskDao { return &BenefitGrantTaskDao{db: global.MysqlEngine} } // BenefitGrantTaskCreate 创建补偿任务,依赖唯一索引 (order_no, benefit_type) 防重复发放。 func (d *BenefitGrantTaskDao) BenefitGrantTaskCreate(ctx context.Context, task *mysqlmodel.BenefitGrantTask) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.BenefitGrantTaskCreate") defer span.End() if err := d.db.WithContext(ctx).Create(task).Error; err != nil { span.RecordError(err) return err } return nil } // BenefitGrantTaskGetByOrderNoAndType 按订单号+权益类型查询补偿任务。 func (d *BenefitGrantTaskDao) BenefitGrantTaskGetByOrderNoAndType(ctx context.Context, orderNo string, benefitType int8) (*mysqlmodel.BenefitGrantTask, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.BenefitGrantTaskGetByOrderNoAndType") defer span.End() var task mysqlmodel.BenefitGrantTask if err := d.db.WithContext(ctx). Where("order_no = ? AND benefit_type = ?", orderNo, benefitType). First(&task).Error; err != nil { span.RecordError(err) return nil, err } return &task, nil } // BenefitGrantTaskUpdateByID 按主键更新补偿任务字段。 func (d *BenefitGrantTaskDao) BenefitGrantTaskUpdateByID(ctx context.Context, id int64, updates map[string]any) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.BenefitGrantTaskUpdateByID") defer span.End() if err := d.db.WithContext(ctx).Model(&mysqlmodel.BenefitGrantTask{}).Where("id = ?", id).Updates(updates).Error; err != nil { span.RecordError(err) return err } return nil } // BenefitGrantTaskListPending 查询待发放的补偿任务(status=0),按主键升序。 func (d *BenefitGrantTaskDao) BenefitGrantTaskListPending(ctx context.Context, limit int) ([]mysqlmodel.BenefitGrantTask, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.BenefitGrantTaskListPending") defer span.End() var list []mysqlmodel.BenefitGrantTask if err := d.db.WithContext(ctx). Where("status = ?", 0). Order("id ASC"). Limit(limit). Find(&list).Error; err != nil { span.RecordError(err) return nil, err } return list, nil } // BenefitGrantTaskReset 将失败任务重置为待发放(首次发放遇到 failed 记录时复用)。 // 保留 retry_count:不归零,保证重试上限与人工介入机制不被重置绕过; // 调用方应配合冷却期使用,避免高频回调无限重置。 func (d *BenefitGrantTaskDao) BenefitGrantTaskReset(ctx context.Context, id int64) error { ctx, span := global.Tracer.Start(ctx, "mysqldao.BenefitGrantTaskReset") defer span.End() if err := d.db.WithContext(ctx).Model(&mysqlmodel.BenefitGrantTask{}). Where("id = ?", id). Updates(map[string]any{"status": 0, "last_error": ""}).Error; err != nil { span.RecordError(err) return err } return nil } // BenefitGrantTaskClaim 条件认领任务(待发放/失败 -> 发放中),返回是否认领成功,防并发重复发放。 func (d *BenefitGrantTaskDao) BenefitGrantTaskClaim(ctx context.Context, id int64) (bool, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.BenefitGrantTaskClaim") defer span.End() res := d.db.WithContext(ctx).Model(&mysqlmodel.BenefitGrantTask{}). Where("id = ? AND status IN (?, ?)", id, 0, 2). Update("status", 3) if res.Error != nil { span.RecordError(res.Error) return false, res.Error } return res.RowsAffected > 0, nil } // BenefitGrantTaskRecoverStale 将「发放中」且超过一定时间未完成的任务重置为待发放(认领后崩溃的恢复)。 func (d *BenefitGrantTaskDao) BenefitGrantTaskRecoverStale(ctx context.Context, beforeUpdatedAt int64) (int64, error) { ctx, span := global.Tracer.Start(ctx, "mysqldao.BenefitGrantTaskRecoverStale") defer span.End() res := d.db.WithContext(ctx).Model(&mysqlmodel.BenefitGrantTask{}). Where("status = ? AND updated_at < ?", 3, beforeUpdatedAt). Update("status", 0) if res.Error != nil { span.RecordError(res.Error) return 0, res.Error } return res.RowsAffected, nil }