feat: refine member and trade management

This commit is contained in:
richjohnrambo
2026-10-18 18:11:34 +08:00
parent 2b3d855bcc
commit 48f304212e
12 changed files with 1079 additions and 70 deletions
+82 -1
View File
@@ -133,6 +133,12 @@ func (s *sSysAppMember) View(ctx context.Context, in *sysin.AppMemberViewInp) (r
}
func (s *sSysAppMember) Edit(ctx context.Context, in *sysin.AppMemberEditInp) error {
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
return s.edit(ctx, in)
})
}
func (s *sSysAppMember) edit(ctx context.Context, in *sysin.AppMemberEditInp) error {
if in.Id == 0 {
return gerror.New("后台不支持创建会员,请由会员在C端完成注册")
}
@@ -233,7 +239,82 @@ func (s *sSysAppMember) Edit(ctx context.Context, in *sysin.AppMemberEditInp) er
return err
}
}
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "member", Action: "member.edit", TargetType: "app_member", TargetId: in.Id, Before: before, After: g.Map{"username": in.Username, "mobileChanged": in.Mobile != "", "parentId": in.ParentId, "riskStatus": in.RiskStatus, "autoDispatch": in.AutoDispatch, "memberCategory": in.MemberCategory}})
if in.AvailableAmount != nil {
if err = s.setAvailableAmount(ctx, in.Id, *in.AvailableAmount); err != nil {
return err
}
}
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "member", Action: "member.edit", TargetType: "app_member", TargetId: in.Id, Before: before, After: g.Map{"username": in.Username, "mobileChanged": in.Mobile != "", "parentId": in.ParentId, "riskStatus": in.RiskStatus, "autoDispatch": in.AutoDispatch, "memberCategory": in.MemberCategory, "availableAmount": in.AvailableAmount}})
}
func (s *sSysAppMember) setAvailableAmount(ctx context.Context, memberId uint64, target int64) error {
wc := dao.WalletAccount.Columns()
var account *entity.WalletAccount
if err := s.walletModel(ctx).Where(wc.MemberId, memberId).Where(wc.Currency, "CNY").LockUpdate().Scan(&account); err != nil {
return gerror.Wrap(err, "读取会员钱包失败")
}
if account == nil {
if _, err := s.walletModel(ctx).
Fields(wc.MemberId, wc.Currency).
Hook(hook.SaveTenant).
Data(g.Map{wc.MemberId: memberId, wc.Currency: "CNY"}).
Insert(); err != nil {
return gerror.Wrap(err, "初始化会员钱包失败")
}
if err := s.walletModel(ctx).Where(wc.MemberId, memberId).Where(wc.Currency, "CNY").LockUpdate().Scan(&account); err != nil {
return gerror.Wrap(err, "读取会员钱包失败")
}
}
if account == nil {
return gerror.New("会员钱包不存在")
}
if account.AvailableAmount == target {
return nil
}
direction := consts.WalletDirectionIncome
amount := target - account.AvailableAmount
if amount < 0 {
direction = consts.WalletDirectionExpense
amount = -amount
}
now := gtime.Now()
ledgerNo := "WL" + now.Format("YmdHis") + grand.S(7)
bizNo := "ME" + now.Format("YmdHis") + grand.S(7)
idempotencyKey := fmt.Sprintf("member-edit-%d-%d-%s", memberId, now.UnixMilli(), grand.S(6))
lc := dao.WalletLedger.Columns()
if _, 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: ledgerNo, lc.AccountId: account.Id, lc.MemberId: memberId,
lc.BizType: "member_edit", lc.BizNo: bizNo, lc.EntryType: fmt.Sprint(consts.WalletAdjustOther),
lc.Direction: direction, lc.Amount: amount, lc.BeforeAmount: account.AvailableAmount,
lc.AfterAmount: target, lc.Currency: account.Currency, lc.IdempotencyKey: idempotencyKey,
lc.Remark: "后台编辑会员余额", lc.OperatorId: contexts.GetUserId(ctx), lc.OccurredAt: now,
}).Insert(); err != nil {
return gerror.Wrap(err, "写入账变流水失败")
}
walletData := g.Map{wc.AvailableAmount: target, wc.Version: gdb.Raw(wc.Version + "+1")}
if direction == consts.WalletDirectionIncome {
walletData[wc.TotalIncome] = gdb.Raw(wc.TotalIncome + "+" + fmt.Sprint(amount))
} else {
walletData[wc.TotalExpense] = gdb.Raw(wc.TotalExpense + "+" + fmt.Sprint(amount))
}
result, err := s.walletModel(ctx).WherePri(account.Id).Where(wc.Version, account.Version).Data(walletData).Update()
if err != nil {
return gerror.Wrap(err, "更新会员余额失败")
}
rows, _ := result.RowsAffected()
if rows != 1 {
return gerror.New("会员余额已变化,请重试")
}
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{
Module: "wallet", Action: "wallet.balance.edit", TargetType: "wallet_account", TargetId: account.Id,
Before: g.Map{"availableAmount": account.AvailableAmount}, After: g.Map{"availableAmount": target, "ledgerNo": ledgerNo},
Reason: "后台编辑会员余额",
})
}
func (s *sSysAppMember) saveProfile(ctx context.Context, in *sysin.AppMemberEditInp) error {
+2 -1
View File
@@ -41,7 +41,8 @@ func (s *sSysRechargeOrder) baseModel(ctx context.Context) *gdb.Model {
func (s *sSysRechargeOrder) listModel(ctx context.Context) *gdb.Model {
rc, mc, pc, wc, cc := dao.RechargeOrder.Columns(), dao.AppMember.Columns(), dao.AppMemberProfile.Columns(), dao.WalletAccount.Columns(), dao.PaymentChannel.Columns()
ac := dao.AppMemberPaymentAccount.Columns()
realName := fmt.Sprintf("COALESCE(NULLIF(%s,''),(SELECT %s FROM %s pa WHERE pa.%s=%s AND pa.%s IS NULL ORDER BY pa.%s ASC,pa.%s DESC LIMIT 1),'') real_name_mask", col("p", pc.RealNameMask), ac.HolderMask, dao.AppMemberPaymentAccount.Table(), ac.MemberId, col("r", rc.MemberId), ac.DeletedAt, ac.IsDefault, ac.Id)
remarkName := fmt.Sprintf("CASE WHEN %s LIKE '姓名:%%' THEN SUBSTRING_INDEX(%s,'姓名:',-1) ELSE '' END", col("r", rc.Remark), col("r", rc.Remark))
realName := fmt.Sprintf("COALESCE(NULLIF(%s,''),(SELECT NULLIF(%s,'') FROM %s pa WHERE pa.%s=%s AND pa.%s IS NULL ORDER BY pa.%s ASC,pa.%s DESC LIMIT 1),NULLIF(%s,''),'') real_name_mask", col("p", pc.RealNameMask), ac.HolderMask, dao.AppMemberPaymentAccount.Table(), ac.MemberId, col("r", rc.MemberId), ac.DeletedAt, ac.IsDefault, ac.Id, remarkName)
return s.baseModel(ctx).
Fields(col("r", rc.Id), col("r", rc.RechargeNo), col("r", rc.MemberId), col("m", mc.Username), col("m", mc.Mobile), realName, fmt.Sprintf("COALESCE(%s,0) available_amount", col("w", wc.AvailableAmount)), col("r", rc.ChannelId), fmt.Sprintf("COALESCE(%s,'') channel_name", col("c", cc.Name)), col("r", rc.RequestAmount), col("r", rc.PaidAmount), col("r", rc.Currency), col("r", rc.ExternalNo), col("r", rc.VoucherAttachmentIds), col("r", rc.RechargeType), col("r", rc.RechargeStatus), col("r", rc.Remark), col("r", rc.ReviewReasonCode), col("r", rc.ReviewReason), col("r", rc.ApprovalRequestId), col("r", rc.LedgerId), col("r", rc.PaidAt), col("r", rc.ReviewedAt), col("r", rc.CreditedAt), col("r", rc.CreatedAt))
}