diff --git a/app/main/api/desc/admin/admin_agent.api b/app/main/api/desc/admin/admin_agent.api index 4d49416..ad93a8c 100644 --- a/app/main/api/desc/admin/admin_agent.api +++ b/app/main/api/desc/admin/admin_agent.api @@ -106,6 +106,10 @@ service main { @handler AdminUpdateSystemConfig post /system-config (AdminUpdateSystemConfigReq) returns (AdminUpdateSystemConfigResp) + // 代理钱包流水分页查询 + @handler AdminGetAgentWalletTransactionList + get /wallet-transaction/list (AdminGetAgentWalletTransactionListReq) returns (AdminGetAgentWalletTransactionListResp) + } type ( @@ -560,4 +564,36 @@ type ( CommissionSafeMode bool `json:"commission_safe_mode"` // 佣金安全防御模式 } + // 代理钱包流水分页查询请求 + AdminGetAgentWalletTransactionListReq { + Page int64 `form:"page"` // 页码 + PageSize int64 `form:"pageSize"` // 每页数量 + AgentId int64 `form:"agent_id"` // 代理ID + TransactionType *string `form:"transaction_type,optional"` // 交易类型(可选) + CreateTimeStart *string `form:"create_time_start,optional"` // 创建时间开始(可选) + CreateTimeEnd *string `form:"create_time_end,optional"` // 创建时间结束(可选) + } + + // 代理钱包流水列表项 + AgentWalletTransactionListItem { + Id int64 `json:"id"` // 主键 + AgentId int64 `json:"agent_id"` // 代理ID + TransactionType string `json:"transaction_type"` // 交易类型 + Amount float64 `json:"amount"` // 变动金额 + BalanceBefore float64 `json:"balance_before"` // 变动前余额 + BalanceAfter float64 `json:"balance_after"` // 变动后余额 + FrozenBalanceBefore float64 `json:"frozen_balance_before"` // 变动前冻结余额 + FrozenBalanceAfter float64 `json:"frozen_balance_after"` // 变动后冻结余额 + TransactionId *string `json:"transaction_id"` // 关联交易ID + RelatedUserId *int64 `json:"related_user_id"` // 关联用户ID + Remark *string `json:"remark"` // 备注说明 + CreateTime string `json:"create_time"` // 创建时间 + } + + // 代理钱包流水分页查询响应 + AdminGetAgentWalletTransactionListResp { + Total int64 `json:"total"` // 总数 + Items []AgentWalletTransactionListItem `json:"items"` // 列表数据 + } + ) \ No newline at end of file diff --git a/app/main/api/internal/handler/admin_agent/admingetagentwallettransactionlisthandler.go b/app/main/api/internal/handler/admin_agent/admingetagentwallettransactionlisthandler.go new file mode 100644 index 0000000..e675a97 --- /dev/null +++ b/app/main/api/internal/handler/admin_agent/admingetagentwallettransactionlisthandler.go @@ -0,0 +1,29 @@ +package admin_agent + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "tydata-server/app/main/api/internal/logic/admin_agent" + "tydata-server/app/main/api/internal/svc" + "tydata-server/app/main/api/internal/types" + "tydata-server/common/result" + "tydata-server/pkg/lzkit/validator" +) + +func AdminGetAgentWalletTransactionListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.AdminGetAgentWalletTransactionListReq + if err := httpx.Parse(r, &req); err != nil { + result.ParamErrorResult(r, w, err) + return + } + if err := validator.Validate(req); err != nil { + result.ParamValidateErrorResult(r, w, err) + return + } + l := admin_agent.NewAdminGetAgentWalletTransactionListLogic(r.Context(), svcCtx) + resp, err := l.AdminGetAgentWalletTransactionList(&req) + result.HttpResult(r, w, resp, err) + } +} diff --git a/app/main/api/internal/handler/routes.go b/app/main/api/internal/handler/routes.go index 570e990..2f0d804 100644 --- a/app/main/api/internal/handler/routes.go +++ b/app/main/api/internal/handler/routes.go @@ -142,6 +142,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/system-config", Handler: admin_agent.AdminUpdateSystemConfigHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/wallet-transaction/list", + Handler: admin_agent.AdminGetAgentWalletTransactionListHandler(serverCtx), + }, { Method: http.MethodGet, Path: "/wallet/:agent_id", diff --git a/app/main/api/internal/logic/admin_agent/admingetagentwallettransactionlistlogic.go b/app/main/api/internal/logic/admin_agent/admingetagentwallettransactionlistlogic.go new file mode 100644 index 0000000..21d99c3 --- /dev/null +++ b/app/main/api/internal/logic/admin_agent/admingetagentwallettransactionlistlogic.go @@ -0,0 +1,84 @@ +package admin_agent + +import ( + "context" + "errors" + + "tydata-server/app/main/api/internal/svc" + "tydata-server/app/main/api/internal/types" + + "github.com/Masterminds/squirrel" + "github.com/zeromicro/go-zero/core/logx" +) + +type AdminGetAgentWalletTransactionListLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewAdminGetAgentWalletTransactionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentWalletTransactionListLogic { + return &AdminGetAgentWalletTransactionListLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *AdminGetAgentWalletTransactionListLogic) AdminGetAgentWalletTransactionList(req *types.AdminGetAgentWalletTransactionListReq) (resp *types.AdminGetAgentWalletTransactionListResp, err error) { + builder := l.svcCtx.AgentWalletTransactionModel.SelectBuilder() + + // 必须传入代理ID + if req.AgentId == 0 { + return nil, errors.New("代理ID不能为空") + } + + builder = builder.Where(squirrel.Eq{"agent_id": req.AgentId}) + + // 可选条件 + if req.TransactionType != nil && *req.TransactionType != "" { + builder = builder.Where(squirrel.Eq{"transaction_type": *req.TransactionType}) + } + if req.CreateTimeStart != nil && *req.CreateTimeStart != "" { + builder = builder.Where(squirrel.GtOrEq{"create_time": *req.CreateTimeStart}) + } + if req.CreateTimeEnd != nil && *req.CreateTimeEnd != "" { + builder = builder.Where(squirrel.LtOrEq{"create_time": *req.CreateTimeEnd}) + } + + list, total, err := l.svcCtx.AgentWalletTransactionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC") + if err != nil { + return nil, err + } + + items := make([]types.AgentWalletTransactionListItem, 0, len(list)) + for _, v := range list { + item := types.AgentWalletTransactionListItem{ + Id: v.Id, + AgentId: v.AgentId, + TransactionType: v.TransactionType, + Amount: v.Amount, + BalanceBefore: v.BalanceBefore, + BalanceAfter: v.BalanceAfter, + FrozenBalanceBefore: v.FrozenBalanceBefore, + FrozenBalanceAfter: v.FrozenBalanceAfter, + CreateTime: v.CreateTime.Format("2006-01-02 15:04:05"), + } + if v.TransactionId.Valid { + item.TransactionId = &v.TransactionId.String + } + if v.RelatedUserId.Valid { + item.RelatedUserId = &v.RelatedUserId.Int64 + } + if v.Remark.Valid { + item.Remark = &v.Remark.String + } + items = append(items, item) + } + + resp = &types.AdminGetAgentWalletTransactionListResp{ + Total: total, + Items: items, + } + return +} diff --git a/app/main/api/internal/logic/admin_agent/adminreviewbankcardwithdrawallogic.go b/app/main/api/internal/logic/admin_agent/adminreviewbankcardwithdrawallogic.go index 5ea45c3..426cf6c 100644 --- a/app/main/api/internal/logic/admin_agent/adminreviewbankcardwithdrawallogic.go +++ b/app/main/api/internal/logic/admin_agent/adminreviewbankcardwithdrawallogic.go @@ -160,11 +160,34 @@ func (l *AdminReviewBankCardWithdrawalLogic) approveBankCardWithdrawal(ctx conte return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err) } + // 记录变动前的冻结余额 + frozenBalanceBefore := wallet.FrozenBalance + + // 更新钱包(减少冻结余额) wallet.FrozenBalance -= record.Amount if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err) } + // 记录交易流水(提现成功) + err = l.svcCtx.AgentService.CreateWalletTransaction( + ctx, + session, + wallet.AgentId, + model.WalletTransactionTypeWithdraw, + -record.Amount, // 变动金额(负数表示减少) + wallet.Balance, // 变动前余额(不变) + wallet.Balance, // 变动后余额(不变) + frozenBalanceBefore, // 变动前冻结余额 + wallet.FrozenBalance, // 变动后冻结余额 + record.WithdrawNo, // 关联交易ID + 0, // 关联用户ID + "提现审核通过", // 备注 + ) + if err != nil { + return err + } + // 更新扣税记录状态为成功 taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id) if err != nil { @@ -206,12 +229,36 @@ func (l *AdminReviewBankCardWithdrawalLogic) rejectWithdrawal(ctx context.Contex return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err) } + // 记录变动前的余额 + balanceBefore := wallet.Balance + frozenBalanceBefore := wallet.FrozenBalance + + // 更新钱包(余额增加,冻结余额减少) wallet.Balance += record.Amount wallet.FrozenBalance -= record.Amount if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err) } + // 记录交易流水(解冻) + err = l.svcCtx.AgentService.CreateWalletTransaction( + ctx, + session, + wallet.AgentId, + model.WalletTransactionTypeUnfreeze, + record.Amount, // 变动金额(正数表示增加) + balanceBefore, // 变动前余额 + wallet.Balance, // 变动后余额 + frozenBalanceBefore, // 变动前冻结余额 + wallet.FrozenBalance, // 变动后冻结余额 + record.WithdrawNo, // 关联交易ID + 0, // 关联用户ID + "提现拒绝,解冻资金", // 备注 + ) + if err != nil { + return err + } + // 更新扣税记录状态为失败 taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id) if err != nil { @@ -244,11 +291,34 @@ func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalSuccess(ctx conte return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err) } + // 记录变动前的冻结余额 + frozenBalanceBefore := wallet.FrozenBalance + + // 更新钱包(减少冻结余额) wallet.FrozenBalance -= record.Amount if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err) } + // 记录交易流水(提现成功) + err = l.svcCtx.AgentService.CreateWalletTransaction( + ctx, + session, + wallet.AgentId, + model.WalletTransactionTypeWithdraw, + -record.Amount, // 变动金额(负数表示减少) + wallet.Balance, // 变动前余额(不变) + wallet.Balance, // 变动后余额(不变) + frozenBalanceBefore, // 变动前冻结余额 + wallet.FrozenBalance, // 变动后冻结余额 + record.WithdrawNo, // 关联交易ID + 0, // 关联用户ID + "提现成功", // 备注 + ) + if err != nil { + return err + } + // 更新扣税记录状态为成功 taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id) if err != nil { @@ -290,12 +360,36 @@ func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalFailure(ctx conte return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err) } + // 记录变动前的余额 + balanceBefore := wallet.Balance + frozenBalanceBefore := wallet.FrozenBalance + + // 更新钱包(余额增加,冻结余额减少) wallet.Balance += record.Amount wallet.FrozenBalance -= record.Amount if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err) } + // 记录交易流水(解冻) + err = l.svcCtx.AgentService.CreateWalletTransaction( + ctx, + session, + wallet.AgentId, + model.WalletTransactionTypeUnfreeze, + record.Amount, // 变动金额(正数表示增加) + balanceBefore, // 变动前余额 + wallet.Balance, // 变动后余额 + frozenBalanceBefore, // 变动前冻结余额 + wallet.FrozenBalance, // 变动后冻结余额 + record.WithdrawNo, // 关联交易ID + 0, // 关联用户ID + "提现失败,解冻资金", // 备注 + ) + if err != nil { + return err + } + // 更新扣税记录状态为失败 taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id) if err != nil { diff --git a/app/main/api/internal/logic/admin_agent/adminupdateagentwalletbalancelogic.go b/app/main/api/internal/logic/admin_agent/adminupdateagentwalletbalancelogic.go index 2106afe..5ea4cb4 100644 --- a/app/main/api/internal/logic/admin_agent/adminupdateagentwalletbalancelogic.go +++ b/app/main/api/internal/logic/admin_agent/adminupdateagentwalletbalancelogic.go @@ -56,6 +56,10 @@ func (l *AdminUpdateAgentWalletBalanceLogic) AdminUpdateAgentWalletBalance(req * // 更新余额 updateErr := l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error { + // 记录变动前的余额 + balanceBefore := wallet.Balance + frozenBalanceBefore := wallet.FrozenBalance + // 使用版本号更新 wallet.Balance = newBalance err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet) @@ -63,9 +67,29 @@ func (l *AdminUpdateAgentWalletBalanceLogic) AdminUpdateAgentWalletBalance(req * return err } - // TODO: 这里可以添加记录余额变更日志的逻辑 + // 创建钱包交易流水记录(手动调整) + remark := fmt.Sprintf("管理员手动调整余额,金额: %.2f", req.Amount) + transErr := l.svcCtx.AgentService.CreateWalletTransaction( + transCtx, + session, + req.AgentId, + model.WalletTransactionTypeAdjust, + req.Amount, // 变动金额(正数表示增加,负数表示减少) + balanceBefore, // 变动前余额 + wallet.Balance, // 变动后余额 + frozenBalanceBefore, // 变动前冻结余额 + wallet.FrozenBalance, // 变动后冻结余额(保持不变) + "", // 关联交易ID(无关联) + 0, // 关联用户ID(无关联) + remark, // 备注 + ) + if transErr != nil { + l.Logger.Errorf("创建代理钱包流水记录失败: %+v", transErr) + return transErr + } + l.Logger.Infof("代理钱包余额变更 - AgentId: %d, 原余额: %.2f, 变更金额: %.2f, 新余额: %.2f", - req.AgentId, wallet.Balance-req.Amount, req.Amount, newBalance) + req.AgentId, balanceBefore, req.Amount, wallet.Balance) return nil }) diff --git a/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go b/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go index 14ddfb5..b66dfaf 100644 --- a/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go +++ b/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go @@ -189,11 +189,36 @@ func (l *BankCardWithdrawalLogic) createBankCardWithdrawalRecord(session sqlx.Se // 冻结资金(事务内操作) func (l *BankCardWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *model.AgentWallet, amount float64) error { + // 记录变动前的余额 + balanceBefore := wallet.Balance + frozenBalanceBefore := wallet.FrozenBalance + + // 更新钱包余额 wallet.Balance -= amount wallet.FrozenBalance += amount err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet) if err != nil { return err } + + // 记录交易流水(冻结操作) + err = l.svcCtx.AgentService.CreateWalletTransaction( + l.ctx, + session, + wallet.AgentId, + model.WalletTransactionTypeFreeze, + amount, // 变动金额 + balanceBefore, // 变动前余额 + wallet.Balance, // 变动后余额 + frozenBalanceBefore, // 变动前冻结余额 + wallet.FrozenBalance, // 变动后冻结余额 + "", // 关联交易ID(暂时为空,创建提现记录后可以更新) + 0, // 关联用户ID + "提现申请冻结资金", // 备注 + ) + if err != nil { + return err + } + return nil } diff --git a/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go b/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go index 3f4299d..28d4a17 100644 --- a/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go +++ b/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go @@ -32,7 +32,10 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service } for _, commission := range commissions { + // 记录更新佣金状态前的状态值,用于后续判断 + oldCommissionStatus := commission.Status commission.Status = 2 // 设置为已退款 + // 更新佣金状态到数据库 var updateCommissionErr error if session != nil { @@ -52,17 +55,30 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service continue } - // 根据订单金额优先减少冻结金额,如果冻结金额不足则减少钱包余额 - if wallet.FrozenBalance >= order.Amount { - // 冻结余额足够,优先减少冻结金额 - wallet.FrozenBalance -= order.Amount - } else { - // 冻结余额不足,先扣减所有冻结金额,再扣减余额 - remaining := order.Amount - wallet.FrozenBalance - wallet.FrozenBalance = 0 - wallet.Balance -= remaining - } + // 记录变动前的余额 + balanceBefore := wallet.Balance + frozenBalanceBefore := wallet.FrozenBalance + // 如果是已结算状态的佣金(status = 1),直接扣减钱包余额 + if oldCommissionStatus == 1 { + // 冻结状态的佣金,根据订单金额优先减少冻结金额,如果冻结金额不足则减少钱包余额 + if wallet.FrozenBalance >= order.Amount { + // 冻结余额足够,优先减少冻结金额 + wallet.FrozenBalance -= order.Amount + } else { + // 冻结余额不足,先扣减所有冻结金额,再扣减余额 + remaining := order.Amount - wallet.FrozenBalance + wallet.FrozenBalance = 0 + wallet.Balance -= remaining + } + // 其他状态的佣金,比如已结算状态(status = 1),直接扣减钱包余额 + } else { + wallet.Balance -= order.Amount + } + // 变动后余额和冻结余额 + balanceAfter := wallet.Balance + frozenBalanceAfter := wallet.FrozenBalance + // 更新钱包 var updateWalletErr error if session != nil { updateWalletErr = svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet) @@ -73,6 +89,26 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service logx.Errorf("更新代理钱包失败,代理ID: %d, 错误: %v", commission.AgentId, updateWalletErr) continue } + // 创建钱包交易流水记录(退款) + transErr := svcCtx.AgentService.CreateWalletTransaction( + ctx, + session, + commission.AgentId, + model.WalletTransactionTypeRefund, + -order.Amount, + balanceBefore, + balanceAfter, + frozenBalanceBefore, + frozenBalanceAfter, + order.OrderNo, + commission.Id, + "订单退款,佣金已扣除", + ) + if transErr != nil { + logx.Errorf("创建代理钱包流水记录失败,代理ID: %d, 错误: %v", commission.AgentId, transErr) + continue + } + } return nil } diff --git a/app/main/api/internal/service/agentService.go b/app/main/api/internal/service/agentService.go index effb19e..c3f79e5 100644 --- a/app/main/api/internal/service/agentService.go +++ b/app/main/api/internal/service/agentService.go @@ -3,6 +3,7 @@ package service import ( "context" "database/sql" + "fmt" "tydata-server/app/main/api/internal/config" "tydata-server/app/main/model" "tydata-server/common/globalkey" @@ -32,6 +33,7 @@ type AgentService struct { AgentPlatformDeductionModel model.AgentPlatformDeductionModel AgentActiveStatModel model.AgentActiveStatModel AgentWithdrawalModel model.AgentWithdrawalModel + AgentWalletTransactionModel model.AgentWalletTransactionModel AsynqService *AsynqService } @@ -42,7 +44,7 @@ func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel mo agentMembershipRechargeOrderModel model.AgentMembershipRechargeOrderModel, agentMembershipUserConfigModel model.AgentMembershipUserConfigModel, agentProductConfigModel model.AgentProductConfigModel, agentPlatformDeductionModel model.AgentPlatformDeductionModel, - agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel, asynqService *AsynqService) *AgentService { + agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel, agentWalletTransactionModel model.AgentWalletTransactionModel, asynqService *AsynqService) *AgentService { return &AgentService{ config: c, @@ -63,6 +65,7 @@ func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel mo AgentPlatformDeductionModel: agentPlatformDeductionModel, AgentActiveStatModel: agentActiveStatModel, AgentWithdrawalModel: agentWithdrawalModel, + AgentWalletTransactionModel: agentWalletTransactionModel, AsynqService: asynqService, } } @@ -142,6 +145,11 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err if findAgentWalletModelErr != nil { return findAgentWalletModelErr } + + // 记录变动前的余额 + balanceBefore := ancestorWallet.Balance + frozenBalanceBefore := ancestorWallet.FrozenBalance + // 奖励不冻结,直接进入balance ancestorWallet.Balance += ancestorCommissionReward @@ -165,7 +173,7 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err ProductId: order.ProductId, Status: commissionStatus, } - _, insertAncestorCommissionErr := l.AgentCommissionModel.Insert(transCtx, session, &ancestorCommissionRecord) + insertResult, insertAncestorCommissionErr := l.AgentCommissionModel.Insert(transCtx, session, &ancestorCommissionRecord) if insertAncestorCommissionErr != nil { return insertAncestorCommissionErr } @@ -175,6 +183,32 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err if updateErr != nil { return updateErr } + + // 获取新插入的佣金记录ID + commissionID, err := insertResult.LastInsertId() + if err != nil { + return err + } + commissionIDStr := fmt.Sprintf("%d", commissionID) // 转换为字符串 + + // 记录交易流水(佣金收入) + transErr := l.CreateWalletTransaction( + transCtx, + session, + AncestorId, + model.WalletTransactionTypeCommission, + ancestorCommissionAmount, // 变动金额(正数表示增加) + balanceBefore, // 变动前余额 + ancestorWallet.Balance, // 变动后余额 + frozenBalanceBefore, // 变动前冻结余额 + ancestorWallet.FrozenBalance, // 变动后冻结余额 + order.OrderNo, // 关联交易ID(订单号) + commissionID, // 关联佣金记录ID + fmt.Sprintf("订单佣金收入,佣金记录ID: %s", commissionIDStr), // 备注(包含佣金记录ID) + ) + if transErr != nil { + return transErr + } } } @@ -236,6 +270,10 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order // 推广人最终获得代理佣金 finalCommission := order.Amount - deductedAmount + // 记录变动前的余额 + balanceBefore := agentWalletModel.Balance + frozenBalanceBefore := agentWalletModel.FrozenBalance + // 根据安全防御模式配置决定佣金状态和钱包操作 if l.config.SystemConfig.CommissionSafeMode { // 安全防御模式:佣金冻结在frozen_balance中 @@ -271,10 +309,31 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order } _ = commissionID // 暂时忽略该变量,因为我们使用其他方式获取佣金记录 + // 更新钱包 updateAgentWalletErr := l.AgentWalletModel.UpdateWithVersion(ctx, session, agentWalletModel) if updateAgentWalletErr != nil { return updateAgentWalletErr } + + // 记录交易流水(佣金收入) + transErr := l.CreateWalletTransaction( + ctx, + session, + agentID, + model.WalletTransactionTypeCommission, + finalCommission, // 变动金额(正数表示增加) + balanceBefore, // 变动前余额 + agentWalletModel.Balance, // 变动后余额 + frozenBalanceBefore, // 变动前冻结余额 + agentWalletModel.FrozenBalance, // 变动后冻结余额 + order.OrderNo, // 关联交易ID(订单号) + 0, // 关联用户ID + "订单佣金收入", // 备注 + ) + if transErr != nil { + return transErr + } + return nil } @@ -519,7 +578,7 @@ func (l *AgentService) GiveUpgradeReward(ctx context.Context, agentID int64, old Type: rewardType, } - _, err = l.AgentRewardsModel.Insert(ctx, session, &agentRewards) + insertResult, err := l.AgentRewardsModel.Insert(ctx, session, &agentRewards) if err != nil { return err } @@ -530,12 +589,42 @@ func (l *AgentService) GiveUpgradeReward(ctx context.Context, agentID int64, old return err } + // 记录变动前的余额 + balanceBefore := ancestorWallet.Balance + frozenBalanceBefore := ancestorWallet.FrozenBalance + ancestorWallet.Balance += rewardAmount ancestorWallet.TotalEarnings += rewardAmount err = l.AgentWalletModel.UpdateWithVersion(ctx, session, ancestorWallet) if err != nil { return err } + + // 获取新插入的奖励记录ID + rewardID, err := insertResult.LastInsertId() + if err != nil { + return err + } + rewardIDStr := fmt.Sprintf("%d", rewardID) // 转换为字符串 + + // 记录交易流水(奖励收入) + transErr := l.CreateWalletTransaction( + ctx, + session, + ancestorID, + model.WalletTransactionTypeReward, + rewardAmount, // 变动金额(正数表示增加) + balanceBefore, // 变动前余额 + ancestorWallet.Balance, // 变动后余额 + frozenBalanceBefore, // 变动前冻结余额 + ancestorWallet.FrozenBalance, // 变动后冻结余额 + rewardIDStr, // 关联交易ID(奖励记录ID) + agentID, // 关联用户ID(下级代理ID) + "下级升级奖励", // 备注 + ) + if transErr != nil { + return transErr + } } return nil @@ -599,7 +688,7 @@ func (l *AgentService) GiveWithdrawReward(ctx context.Context, agentID int64, wi Type: model.AgentRewardsTypeDescendantWithdraw, } - _, err = l.AgentRewardsModel.Insert(ctx, session, &agentRewards) + insertResult, err := l.AgentRewardsModel.Insert(ctx, session, &agentRewards) if err != nil { return err } @@ -610,12 +699,42 @@ func (l *AgentService) GiveWithdrawReward(ctx context.Context, agentID int64, wi return err } + // 记录变动前的余额 + balanceBefore := ancestorWallet.Balance + frozenBalanceBefore := ancestorWallet.FrozenBalance + ancestorWallet.Balance += rewardAmount ancestorWallet.TotalEarnings += rewardAmount err = l.AgentWalletModel.UpdateWithVersion(ctx, session, ancestorWallet) if err != nil { return err } + + // 获取新插入的奖励记录ID + rewardID, err := insertResult.LastInsertId() + if err != nil { + return err + } + rewardIDStr := fmt.Sprintf("%d", rewardID) // 转换为字符串 + + // 记录交易流水(奖励收入) + transErr := l.CreateWalletTransaction( + ctx, + session, + ancestorID, + model.WalletTransactionTypeReward, + rewardAmount, // 变动金额(正数表示增加) + balanceBefore, // 变动前余额 + ancestorWallet.Balance, // 变动后余额 + frozenBalanceBefore, // 变动前冻结余额 + ancestorWallet.FrozenBalance, // 变动后冻结余额 + rewardIDStr, // 关联交易ID(奖励记录ID) + agentID, // 关联用户ID(下级代理ID) + "下级提现奖励", // 备注 + ) + if transErr != nil { + return transErr + } } } @@ -675,3 +794,57 @@ func (l *AgentService) RetryAgentProcess(ctx context.Context, orderID int64) err // 执行代理处理 return l.AgentProcess(ctx, order) } + +// CreateWalletTransaction 创建代理钱包流水记录 +// ctx: 上下文 +// session: 数据库会话(事务) +// agentID: 代理ID +// transactionType: 交易类型 (commission/withdraw/freeze/unfreeze/reward/refund/adjust) +// amount: 变动金额(正数为增加,负数为减少) +// balanceBefore: 变动前余额 +// balanceAfter: 变动后余额 +// frozenBalanceBefore: 变动前冻结余额 +// frozenBalanceAfter: 变动后冻结余额 +// transactionID: 关联交易ID(订单号、提现申请号等) +// relatedUserID: 关联用户ID(如佣金来源用户) +// remark: 备注说明 +func (l *AgentService) CreateWalletTransaction(ctx context.Context, session sqlx.Session, + agentID int64, transactionType string, amount float64, + balanceBefore, balanceAfter, frozenBalanceBefore, frozenBalanceAfter float64, + transactionID string, relatedUserID int64, remark string) error { + + // 处理可空字段 + var transactionIDField sql.NullString + if transactionID != "" { + transactionIDField = sql.NullString{String: transactionID, Valid: true} + } + + var relatedUserIDField sql.NullInt64 + if relatedUserID > 0 { + relatedUserIDField = sql.NullInt64{Int64: relatedUserID, Valid: true} + } + + var remarkField sql.NullString + if remark != "" { + remarkField = sql.NullString{String: remark, Valid: true} + } + + transaction := &model.AgentWalletTransaction{ + AgentId: agentID, + TransactionType: transactionType, + Amount: amount, + BalanceBefore: balanceBefore, + BalanceAfter: balanceAfter, + FrozenBalanceBefore: frozenBalanceBefore, + FrozenBalanceAfter: frozenBalanceAfter, + TransactionId: transactionIDField, + RelatedUserId: relatedUserIDField, + Remark: remarkField, + } + + _, err := l.AgentWalletTransactionModel.Insert(ctx, session, transaction) + if err != nil { + return errors.Wrapf(err, "创建代理钱包流水记录失败,agentID: %d, type: %s, amount: %.2f", agentID, transactionType, amount) + } + return nil +} diff --git a/app/main/api/internal/service/authorizationService_test.go b/app/main/api/internal/service/authorizationService_test.go index 45db295..eb2ae8f 100644 --- a/app/main/api/internal/service/authorizationService_test.go +++ b/app/main/api/internal/service/authorizationService_test.go @@ -254,7 +254,7 @@ func TestGenerateAuthorizationDocument(t *testing.T) { assert.False(t, authDoc.ExpireTime.Valid) // 永久保留,不设置过期时间 // 验证文件路径格式(兼容Windows和Unix路径分隔符) - assert.True(t, strings.Contains(authDoc.FilePath, "data/authorization_docs") || + assert.True(t, strings.Contains(authDoc.FilePath, "data/authorization_docs") || strings.Contains(authDoc.FilePath, "data\\authorization_docs")) assert.Contains(t, authDoc.FileName, "auth_") assert.Contains(t, authDoc.FileName, ".pdf") @@ -372,7 +372,7 @@ func TestGeneratePDFContent(t *testing.T) { t.Logf("📁 文件路径: %s", filePath) t.Logf("🔗 相对路径: %s", relativePath) t.Logf("📊 文件大小: %d 字节", len(pdfBytes)) - + // 获取绝对路径 absPath, _ := filepath.Abs(filePath) t.Logf("📍 绝对路径: %s", absPath) @@ -430,16 +430,16 @@ func TestSavePDFToLocal(t *testing.T) { t.Logf("📁 文件路径: %s", filePath) t.Logf("🔗 相对路径: %s", relativePath) t.Logf("📊 文件大小: %d 字节", len(pdfBytes)) - + // 获取绝对路径 absPath, _ := filepath.Abs(filePath) t.Logf("📍 绝对路径: %s", absPath) - + // 验证文件内容 fileInfo, err := os.Stat(filePath) assert.NoError(t, err) assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB - + t.Logf("🎉 文件保存验证通过!") } else { t.Errorf("❌ PDF文件保存失败: %v", err) @@ -648,18 +648,18 @@ func TestGeneratePDFFile(t *testing.T) { t.Logf("📁 文件路径: %s", authDoc.FilePath) t.Logf("🔗 相对路径: %s", authDoc.FileUrl) t.Logf("📊 文件大小: %d 字节", authDoc.FileSize) - + // 验证文件内容 fileInfo, err := os.Stat(authDoc.FilePath) assert.NoError(t, err) assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB - + // 验证文件名格式 assert.Regexp(t, `^auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileName) - + // 验证路径格式 assert.Regexp(t, `^\d{4}/\d{2}/auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileUrl) - + t.Logf("🎉 所有验证通过!") } else { t.Errorf("❌ PDF文件未创建: %s", authDoc.FilePath) diff --git a/app/main/api/internal/svc/servicecontext.go b/app/main/api/internal/svc/servicecontext.go index c2df6ed..4d646f8 100644 --- a/app/main/api/internal/svc/servicecontext.go +++ b/app/main/api/internal/svc/servicecontext.go @@ -63,6 +63,7 @@ type ServiceContext struct { AgentRealNameModel model.AgentRealNameModel AgentWithdrawalTaxModel model.AgentWithdrawalTaxModel AgentWithdrawalTaxExemptionModel model.AgentWithdrawalTaxExemptionModel + AgentWalletTransactionModel model.AgentWalletTransactionModel // 管理后台相关模型 AdminApiModel model.AdminApiModel @@ -152,6 +153,7 @@ func NewServiceContext(c config.Config) *ServiceContext { agentRealNameModel := model.NewAgentRealNameModel(db, cacheConf) agentWithdrawalTaxModel := model.NewAgentWithdrawalTaxModel(db, cacheConf) agentWithdrawalTaxExemptionModel := model.NewAgentWithdrawalTaxExemptionModel(db, cacheConf) + agentWalletTransactionModel := model.NewAgentWalletTransactionModel(db, cacheConf) // ============================== 管理后台相关模型 ============================== adminApiModel := model.NewAdminApiModel(db, cacheConf) adminMenuModel := model.NewAdminMenuModel(db, cacheConf) @@ -194,7 +196,7 @@ func NewServiceContext(c config.Config) *ServiceContext { agentCommissionModel, agentCommissionDeductionModel, agentWalletModel, agentLinkModel, agentOrderModel, agentRewardsModel, agentMembershipConfigModel, agentMembershipRechargeOrderModel, agentMembershipUserConfigModel, agentProductConfigModel, agentPlatformDeductionModel, - agentActiveStatModel, agentWithdrawalModel, asynqService) + agentActiveStatModel, agentWithdrawalModel, agentWalletTransactionModel, asynqService) userService := service.NewUserService(&c, userModel, userAuthModel, userTempModel, agentModel) dictService := service.NewDictService(adminDictTypeModel, adminDictDataModel) adminPromotionLinkStatsService := service.NewAdminPromotionLinkStatsService(adminPromotionLinkModel, @@ -261,6 +263,7 @@ func NewServiceContext(c config.Config) *ServiceContext { AgentRealNameModel: agentRealNameModel, AgentWithdrawalTaxModel: agentWithdrawalTaxModel, AgentWithdrawalTaxExemptionModel: agentWithdrawalTaxExemptionModel, + AgentWalletTransactionModel: agentWalletTransactionModel, // 管理后台相关模型 AdminApiModel: adminApiModel, diff --git a/app/main/api/internal/types/types.go b/app/main/api/internal/types/types.go index c083c68..187b634 100644 --- a/app/main/api/internal/types/types.go +++ b/app/main/api/internal/types/types.go @@ -363,6 +363,20 @@ type AdminGetAgentWalletResp struct { TotalEarnings float64 `json:"total_earnings"` // 总收益 } +type AdminGetAgentWalletTransactionListReq struct { + Page int64 `form:"page"` // 页码 + PageSize int64 `form:"pageSize"` // 每页数量 + AgentId int64 `form:"agent_id"` // 代理ID + TransactionType *string `form:"transaction_type,optional"` // 交易类型(可选) + CreateTimeStart *string `form:"create_time_start,optional"` // 创建时间开始(可选) + CreateTimeEnd *string `form:"create_time_end,optional"` // 创建时间结束(可选) +} + +type AdminGetAgentWalletTransactionListResp struct { + Total int64 `json:"total"` // 总数 + Items []AgentWalletTransactionListItem `json:"items"` // 列表数据 +} + type AdminGetAgentWithdrawalListReq struct { Page int64 `form:"page"` // 页码 PageSize int64 `form:"pageSize"` // 每页数量 @@ -1261,6 +1275,21 @@ type AgentSubordinateList struct { TotalContribution float64 `json:"total_contribution"` // 总贡献 } +type AgentWalletTransactionListItem struct { + Id int64 `json:"id"` // 主键 + AgentId int64 `json:"agent_id"` // 代理ID + TransactionType string `json:"transaction_type"` // 交易类型 + Amount float64 `json:"amount"` // 变动金额 + BalanceBefore float64 `json:"balance_before"` // 变动前余额 + BalanceAfter float64 `json:"balance_after"` // 变动后余额 + FrozenBalanceBefore float64 `json:"frozen_balance_before"` // 变动前冻结余额 + FrozenBalanceAfter float64 `json:"frozen_balance_after"` // 变动后冻结余额 + TransactionId *string `json:"transaction_id"` // 关联交易ID + RelatedUserId *int64 `json:"related_user_id"` // 关联用户ID + Remark *string `json:"remark"` // 备注说明 + CreateTime string `json:"create_time"` // 创建时间 +} + type AgentWithdrawalListItem struct { Id int64 `json:"id"` // 主键 AgentId int64 `json:"agent_id"` // 代理ID diff --git a/app/main/model/agentWalletTransactionModel.go b/app/main/model/agentWalletTransactionModel.go new file mode 100644 index 0000000..55eba0b --- /dev/null +++ b/app/main/model/agentWalletTransactionModel.go @@ -0,0 +1,27 @@ +package model + +import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) + +var _ AgentWalletTransactionModel = (*customAgentWalletTransactionModel)(nil) + +type ( + // AgentWalletTransactionModel is an interface to be customized, add more methods here, + // and implement the added methods in customAgentWalletTransactionModel. + AgentWalletTransactionModel interface { + agentWalletTransactionModel + } + + customAgentWalletTransactionModel struct { + *defaultAgentWalletTransactionModel + } +) + +// NewAgentWalletTransactionModel returns a model for the database table. +func NewAgentWalletTransactionModel(conn sqlx.SqlConn, c cache.CacheConf) AgentWalletTransactionModel { + return &customAgentWalletTransactionModel{ + defaultAgentWalletTransactionModel: newAgentWalletTransactionModel(conn, c), + } +} diff --git a/app/main/model/agentWalletTransactionModel_gen.go b/app/main/model/agentWalletTransactionModel_gen.go new file mode 100644 index 0000000..856c201 --- /dev/null +++ b/app/main/model/agentWalletTransactionModel_gen.go @@ -0,0 +1,377 @@ +// Code generated by goctl. DO NOT EDIT! + +package model + +import ( + "context" + "database/sql" + "fmt" + "strings" + + "time" + + "tydata-server/common/globalkey" + + "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" +) + +var ( + agentWalletTransactionFieldNames = builder.RawFieldNames(&AgentWalletTransaction{}) + agentWalletTransactionRows = strings.Join(agentWalletTransactionFieldNames, ",") + agentWalletTransactionRowsExpectAutoSet = strings.Join(stringx.Remove(agentWalletTransactionFieldNames, "`id`", "`create_time`", "`update_time`"), ",") + agentWalletTransactionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentWalletTransactionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" + + cacheTydataAgentWalletTransactionIdPrefix = "cache:tydata:agentWalletTransaction:id:" +) + +type ( + agentWalletTransactionModel interface { + Insert(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) (sql.Result, error) + FindOne(ctx context.Context, id int64) (*AgentWalletTransaction, error) + Update(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) (sql.Result, error) + UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) error + Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error + SelectBuilder() squirrel.SelectBuilder + DeleteSoft(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) error + FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error) + FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error) + FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*AgentWalletTransaction, error) + FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWalletTransaction, error) + FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWalletTransaction, int64, error) + FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentWalletTransaction, error) + FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentWalletTransaction, error) + Delete(ctx context.Context, session sqlx.Session, id int64) error + } + + defaultAgentWalletTransactionModel struct { + sqlc.CachedConn + table string + } + + AgentWalletTransaction struct { + Id int64 `db:"id"` + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` + DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 + DelState int64 `db:"del_state"` + Version int64 `db:"version"` // 版本号 + AgentId int64 `db:"agent_id"` // 代理ID + TransactionType string `db:"transaction_type"` // 交易类型:commission(佣金收入)、withdraw(提现)、freeze(冻结)、unfreeze(解冻)、reward(奖励)、refund(退款)、adjust(调整)等 + Amount float64 `db:"amount"` // 变动金额(正数为增加,负数为减少) + BalanceBefore float64 `db:"balance_before"` // 变动前余额 + BalanceAfter float64 `db:"balance_after"` // 变动后余额 + FrozenBalanceBefore float64 `db:"frozen_balance_before"` // 变动前冻结余额 + FrozenBalanceAfter float64 `db:"frozen_balance_after"` // 变动后冻结余额 + TransactionId sql.NullString `db:"transaction_id"` // 关联交易ID(订单号、提现申请号等) + RelatedUserId sql.NullInt64 `db:"related_user_id"` // 关联用户ID(如佣金来源用户) + Remark sql.NullString `db:"remark"` // 备注说明 + } +) + +func newAgentWalletTransactionModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAgentWalletTransactionModel { + return &defaultAgentWalletTransactionModel{ + CachedConn: sqlc.NewConn(conn, c), + table: "`agent_wallet_transaction`", + } +} + +func (m *defaultAgentWalletTransactionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) (sql.Result, error) { + data.DelState = globalkey.DelStateNo + tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, data.Id) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentWalletTransactionRowsExpectAutoSet) + if session != nil { + return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark) + } + return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark) + }, tydataAgentWalletTransactionIdKey) +} + +func (m *defaultAgentWalletTransactionModel) FindOne(ctx context.Context, id int64) (*AgentWalletTransaction, error) { + tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, id) + var resp AgentWalletTransaction + err := m.QueryRowCtx(ctx, &resp, tydataAgentWalletTransactionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentWalletTransactionRows, m.table) + return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultAgentWalletTransactionModel) Update(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) (sql.Result, error) { + tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, data.Id) + return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentWalletTransactionRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark, data.Id) + } + return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark, data.Id) + }, tydataAgentWalletTransactionIdKey) +} + +func (m *defaultAgentWalletTransactionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) error { + + oldVersion := data.Version + data.Version += 1 + + var sqlResult sql.Result + var err error + + tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, data.Id) + sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentWalletTransactionRowsWithPlaceHolder) + if session != nil { + return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark, data.Id, oldVersion) + } + return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark, data.Id, oldVersion) + }, tydataAgentWalletTransactionIdKey) + if err != nil { + return err + } + updateCount, err := sqlResult.RowsAffected() + if err != nil { + return err + } + if updateCount == 0 { + return ErrNoRowsUpdate + } + + return nil +} + +func (m *defaultAgentWalletTransactionModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) error { + data.DelState = globalkey.DelStateYes + data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true} + if err := m.UpdateWithVersion(ctx, session, data); err != nil { + return errors.Wrapf(errors.New("delete soft failed "), "AgentWalletTransactionModel delete err : %+v", err) + } + return nil +} + +func (m *defaultAgentWalletTransactionModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field") + } + + builder = builder.Columns("IFNULL(SUM(" + field + "),0)") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp float64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultAgentWalletTransactionModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) { + + if len(field) == 0 { + return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field") + } + + builder = builder.Columns("COUNT(" + field + ")") + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return 0, err + } + + var resp int64 + err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +} + +func (m *defaultAgentWalletTransactionModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*AgentWalletTransaction, error) { + + builder = builder.Columns(agentWalletTransactionRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql() + if err != nil { + return nil, err + } + + var resp []*AgentWalletTransaction + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAgentWalletTransactionModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWalletTransaction, error) { + + builder = builder.Columns(agentWalletTransactionRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AgentWalletTransaction + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAgentWalletTransactionModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWalletTransaction, int64, error) { + + total, err := m.FindCount(ctx, builder, "id") + if err != nil { + return nil, 0, err + } + + builder = builder.Columns(agentWalletTransactionRows) + + if orderBy == "" { + builder = builder.OrderBy("id DESC") + } else { + builder = builder.OrderBy(orderBy) + } + + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, total, err + } + + var resp []*AgentWalletTransaction + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, total, nil + default: + return nil, total, err + } +} + +func (m *defaultAgentWalletTransactionModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentWalletTransaction, error) { + + builder = builder.Columns(agentWalletTransactionRows) + + if preMinId > 0 { + builder = builder.Where(" id < ? ", preMinId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AgentWalletTransaction + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAgentWalletTransactionModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentWalletTransaction, error) { + + builder = builder.Columns(agentWalletTransactionRows) + + if preMaxId > 0 { + builder = builder.Where(" id > ? ", preMaxId) + } + + query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql() + if err != nil { + return nil, err + } + + var resp []*AgentWalletTransaction + err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...) + switch err { + case nil: + return resp, nil + default: + return nil, err + } +} + +func (m *defaultAgentWalletTransactionModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error { + + return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error { + return fn(ctx, session) + }) + +} + +func (m *defaultAgentWalletTransactionModel) SelectBuilder() squirrel.SelectBuilder { + return squirrel.Select().From(m.table) +} +func (m *defaultAgentWalletTransactionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { + tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, id) + _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + if session != nil { + return session.ExecCtx(ctx, query, id) + } + return conn.ExecCtx(ctx, query, id) + }, tydataAgentWalletTransactionIdKey) + return err +} +func (m *defaultAgentWalletTransactionModel) formatPrimary(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, primary) +} +func (m *defaultAgentWalletTransactionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { + query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentWalletTransactionRows, m.table) + return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo) +} + +func (m *defaultAgentWalletTransactionModel) tableName() string { + return m.table +} diff --git a/app/main/model/vars.go b/app/main/model/vars.go index fc458e0..174a22a 100644 --- a/app/main/model/vars.go +++ b/app/main/model/vars.go @@ -108,3 +108,14 @@ const ( TaxStatusExempt = 2 // 免税 TaxStatusFailed = 3 // 扣税失败 ) + +// 钱包交易类型 +const ( + WalletTransactionTypeCommission = "commission" // 佣金收入 + WalletTransactionTypeWithdraw = "withdraw" // 提现 + WalletTransactionTypeFreeze = "freeze" // 冻结 + WalletTransactionTypeUnfreeze = "unfreeze" // 解冻 + WalletTransactionTypeReward = "reward" // 奖励 + WalletTransactionTypeRefund = "refund" // 退款 + WalletTransactionTypeAdjust = "adjust" // 调整 +) diff --git a/deploy/script/gen_models.ps1 b/deploy/script/gen_models.ps1 index 8dfec08..c2cb08c 100644 --- a/deploy/script/gen_models.ps1 +++ b/deploy/script/gen_models.ps1 @@ -11,7 +11,8 @@ $tables = @( # "agent_active_stat", # "agent_audit", # "agent_closure", - "agent_commission" + # "agent_commission" + "agent_wallet_transaction" # "agent_commission_deduction" # "agent_link", # "agent_membership_config",