package sys import ( "context" "fmt" "hotgo/internal/consts" "hotgo/internal/dao" "hotgo/internal/library/contexts" "hotgo/internal/library/hgorm/handler" "hotgo/internal/library/hgorm/hook" "hotgo/internal/model/entity" "hotgo/internal/model/input/sysin" "hotgo/internal/service" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/grand" ) type sSysTaskSubmission struct{} func NewSysTaskSubmission() *sSysTaskSubmission { return &sSysTaskSubmission{} } func init() { service.RegisterSysTaskSubmission(NewSysTaskSubmission()) } func (s *sSysTaskSubmission) Model(ctx context.Context) *gdb.Model { return handler.Model(dao.TaskSubmission.Ctx(ctx), &handler.Option{FilterTenant: true}) } func (s *sSysTaskSubmission) listModel(ctx context.Context) *gdb.Model { sc, tc, oc, mc, lc := dao.TaskSubmission.Columns(), dao.Task.Columns(), dao.TradeOrder.Columns(), dao.AppMember.Columns(), dao.AppMemberLevel.Columns() return s.Model(ctx).As("s"). LeftJoin(dao.Task.Table()+" t", col("t", tc.Id)+"="+col("s", sc.TaskId)). LeftJoin(dao.TradeOrder.Table()+" o", col("o", oc.Id)+"="+col("s", sc.TradeOrderId)). LeftJoin(dao.AppMember.Table()+" m", col("m", mc.Id)+"="+col("s", sc.MemberId)). LeftJoin(dao.AppMemberLevel.Table()+" l", col("l", lc.Id)+"="+col("s", sc.MemberLevelId)). Fields(col("s", sc.Id), col("s", sc.SubmissionNo), col("s", sc.TradeOrderId), fmt.Sprintf("COALESCE(%s,'') trade_order_no", col("o", oc.OrderNo)), fmt.Sprintf("COALESCE(%s,'') product_name", col("o", oc.ProductName)), col("s", sc.TaskId), fmt.Sprintf("COALESCE(NULLIF(%s,''),NULLIF(%s,''),'') task_title", col("t", tc.Title), col("o", oc.ProductName)), col("s", sc.MemberId), col("m", mc.Username), col("m", mc.Mobile), fmt.Sprintf("COALESCE(%s,'') level_name", col("l", lc.Name)), col("s", sc.EvidenceText), col("s", sc.EvidenceAttachmentIds), col("s", sc.SubmissionStatus), col("s", sc.CommissionAmount), col("s", sc.SuccessRate), col("s", sc.ReviewReasonCode), col("s", sc.ReviewReason), col("s", sc.AllowResubmit), col("s", sc.LedgerId), col("s", sc.ReviewedAt), col("s", sc.SettledAt), col("s", sc.CreatedAt)) } func (s *sSysTaskSubmission) filter(ctx context.Context, mod *gdb.Model, in *sysin.TaskSubmissionListInp) (*gdb.Model, error) { sc, mc := dao.TaskSubmission.Columns(), dao.AppMember.Columns() if in.TradeOrderId > 0 { mod = mod.Where(col("s", sc.TradeOrderId), in.TradeOrderId) } if in.Username != "" { mod = mod.WhereLike(col("m", mc.Username), "%"+in.Username+"%") } if in.Mobile != "" { mod = mod.Where(col("m", mc.Mobile), in.Mobile) } if in.SubmissionStatus > 0 { mod = mod.Where(col("s", sc.SubmissionStatus), in.SubmissionStatus) } if len(in.CreatedAt) == 2 { mod = mod.WhereBetween(col("s", sc.CreatedAt), in.CreatedAt[0], in.CreatedAt[1]) } return mod, nil } func (s *sSysTaskSubmission) List(ctx context.Context, in *sysin.TaskSubmissionListInp) (list []*sysin.TaskSubmissionListModel, total int, err error) { mod, err := s.filter(ctx, s.listModel(ctx), in) if err != nil { return nil, 0, err } if err = mod.Page(in.Page, in.PerPage).OrderDesc(col("s", dao.TaskSubmission.Columns().Id)).ScanAndCount(&list, &total, false); err != nil { return nil, 0, gerror.Wrap(err, "获取会员任务审核列表失败") } return } func (s *sSysTaskSubmission) View(ctx context.Context, in *sysin.TaskSubmissionViewInp) (res *sysin.TaskSubmissionListModel, err error) { if err = s.listModel(ctx).Where(col("s", dao.TaskSubmission.Columns().Id), in.Id).Scan(&res); err != nil { return nil, gerror.Wrap(err, "获取任务提交详情失败") } if res == nil { return nil, gerror.New("任务提交不存在") } return } func (s *sSysTaskSubmission) Review(ctx context.Context, in *sysin.TaskSubmissionReviewInp) error { return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { sc := dao.TaskSubmission.Columns() var submission *entity.TaskSubmission if err := s.Model(ctx).WherePri(in.Id).LockUpdate().Scan(&submission); err != nil { return err } if submission == nil { return gerror.New("任务提交不存在") } if submission.SubmissionStatus != consts.TaskSubmissionPending { return gerror.New("该记录已审核,请勿重复操作") } if submission.TaskId > 0 { var task *entity.Task if err := handler.Model(dao.Task.Ctx(ctx), &handler.Option{FilterTenant: true}).WherePri(submission.TaskId).Scan(&task); err != nil { return err } if task == nil { return gerror.New("关联任务不存在") } } if submission.TradeOrderId > 0 { oc := dao.TradeOrder.Columns() count, err := handler.Model(dao.TradeOrder.Ctx(ctx), &handler.Option{FilterTenant: true}).WherePri(submission.TradeOrderId).Where(oc.MemberId, submission.MemberId).Count() if err != nil { return err } if count == 0 { return gerror.New("关联订单不存在或不属于该会员") } } if submission.TaskId == 0 && submission.TradeOrderId == 0 { return gerror.New("提交记录缺少任务和订单关联") } var member *entity.AppMember if err := handler.Model(dao.AppMember.Ctx(ctx), &handler.Option{FilterTenant: true}).WherePri(submission.MemberId).Scan(&member); err != nil { return err } if member == nil || member.MemberStatus != consts.AppMemberStatusNormal { return gerror.New("会员不存在或状态异常") } now := gtime.Now() uid := contexts.GetUserId(ctx) if in.Decision == consts.FinanceDecisionReject { _, err := s.Model(ctx).WherePri(in.Id).Data(g.Map{sc.SubmissionStatus: consts.TaskSubmissionRejected, sc.ReviewedBy: uid, sc.ReviewedAt: now, sc.ReviewReasonCode: in.ReasonCode, sc.ReviewReason: in.ReasonText, sc.AllowResubmit: in.AllowResubmit, sc.UpdatedBy: uid}).Update() if err != nil { return gerror.Wrap(err, "驳回任务提交失败") } return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "task_submission", Action: "task_submission.reject", TargetType: "task_submission", TargetId: submission.Id, Before: submission, After: g.Map{"status": consts.TaskSubmissionRejected, "allowResubmit": in.AllowResubmit}, Reason: in.ReasonText}) } wc := dao.WalletAccount.Columns() var account *entity.WalletAccount if err := handler.Model(dao.WalletAccount.Ctx(ctx), &handler.Option{FilterTenant: true}).Where(wc.MemberId, submission.MemberId).Where(wc.Currency, "CNY").LockUpdate().Scan(&account); err != nil { return err } if account == nil || account.AccountStatus != 1 { return gerror.New("会员钱包不存在或不可结算") } lc := dao.WalletLedger.Columns() count, err := handler.Model(dao.WalletLedger.Ctx(ctx), &handler.Option{FilterTenant: true}).Where(lc.IdempotencyKey, in.IdempotencyKey).Count() if err != nil { return err } if count > 0 { return gerror.New("该幂等键已使用,任务佣金未重复发放") } after := account.AvailableAmount + submission.CommissionAmount result, err := dao.WalletLedger.Ctx(ctx).Fields(lc.LedgerNo, lc.AccountId, lc.MemberId, lc.BizType, lc.BizNo, lc.EntryType, lc.Direction, lc.Amount, lc.BeforeAmount, lc.AfterAmount, lc.Currency, lc.IdempotencyKey, lc.Remark, lc.OperatorId, lc.OccurredAt).Hook(hook.SaveTenant).Data(g.Map{lc.LedgerNo: "WL" + gtime.Now().Format("YmdHis") + grand.S(7), lc.AccountId: account.Id, lc.MemberId: submission.MemberId, lc.BizType: "commission", lc.BizNo: submission.SubmissionNo, lc.EntryType: "available", lc.Direction: consts.WalletDirectionIncome, lc.Amount: submission.CommissionAmount, lc.BeforeAmount: account.AvailableAmount, lc.AfterAmount: after, lc.Currency: account.Currency, lc.IdempotencyKey: in.IdempotencyKey, lc.Remark: in.ReasonText, lc.OperatorId: uid, lc.OccurredAt: now}).Insert() if err != nil { return gerror.Wrap(err, "写入任务佣金台账失败") } ledgerId64, _ := result.LastInsertId() update, err := handler.Model(dao.WalletAccount.Ctx(ctx), &handler.Option{FilterTenant: true}).WherePri(account.Id).Where(wc.Version, account.Version).Data(g.Map{wc.AvailableAmount: after, wc.TotalIncome: gdb.Raw(wc.TotalIncome + "+" + fmt.Sprint(submission.CommissionAmount)), wc.Version: gdb.Raw(wc.Version + "+1")}).Update() if err != nil { return err } rows, _ := update.RowsAffected() if rows != 1 { return gerror.New("钱包余额已变化,请重试") } _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{sc.SubmissionStatus: consts.TaskSubmissionSettled, sc.ReviewedBy: uid, sc.ReviewedAt: now, sc.ReviewReasonCode: in.ReasonCode, sc.ReviewReason: in.ReasonText, sc.AllowResubmit: consts.StatusDisable, sc.LedgerId: uint64(ledgerId64), sc.SettledAt: now, sc.UpdatedBy: uid}).Update() if err != nil { return err } if submission.TradeOrderId > 0 { tc := dao.TradeOrder.Columns() _, err = handler.Model(dao.TradeOrder.Ctx(ctx), &handler.Option{FilterTenant: true}).WherePri(submission.TradeOrderId).Data(g.Map{tc.OrderStatus: consts.TradeOrderCompleted, tc.CompletedAt: now, tc.UpdatedBy: uid}).Update() if err != nil { return err } } return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "task_submission", Action: "task_submission.settle", TargetType: "task_submission", TargetId: submission.Id, Before: submission, After: g.Map{"status": consts.TaskSubmissionSettled, "ledgerId": uint64(ledgerId64), "commissionAmount": submission.CommissionAmount}, Reason: in.ReasonText}) }) }