diff --git a/app/main/api/desc/front/app.api b/app/main/api/desc/front/app.api index 1882504..aa874a7 100644 --- a/app/main/api/desc/front/app.api +++ b/app/main/api/desc/front/app.api @@ -19,6 +19,12 @@ service main { @handler getAppVersion get /app/version returns (getAppVersionResp) + + @doc( + summary: "获取APP全局配置" + ) + @handler getAppConfig + get /app/config returns (GetAppConfigResp) } type ( @@ -34,4 +40,14 @@ type ( Version string `json:"version"` WgtUrl string `json:"wgtUrl"` } -) \ No newline at end of file +) + +type ( + AppQueryConfig { + RetentionDays int64 `json:"retention_days"` // 查询结果保留天数 + } + + GetAppConfigResp { + Query AppQueryConfig `json:"query"` // 查询相关配置 + } +) diff --git a/app/main/api/internal/handler/app/getappconfighandler.go b/app/main/api/internal/handler/app/getappconfighandler.go new file mode 100644 index 0000000..4982dd7 --- /dev/null +++ b/app/main/api/internal/handler/app/getappconfighandler.go @@ -0,0 +1,17 @@ +package app + +import ( + "net/http" + + "bdrp-server/app/main/api/internal/logic/app" + "bdrp-server/app/main/api/internal/svc" + "bdrp-server/common/result" +) + +func GetAppConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + l := app.NewGetAppConfigLogic(r.Context(), svcCtx) + resp, err := l.GetAppConfig() + 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 3247ff7..321a02e 100644 --- a/app/main/api/internal/handler/routes.go +++ b/app/main/api/internal/handler/routes.go @@ -899,6 +899,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { server.AddRoutes( []rest.Route{ + { + // 获取APP全局配置 + Method: http.MethodGet, + Path: "/app/config", + Handler: app.GetAppConfigHandler(serverCtx), + }, { Method: http.MethodGet, Path: "/app/version", diff --git a/app/main/api/internal/logic/admin_agent/adminbatchunfreezeagentcommissionlogic.go b/app/main/api/internal/logic/admin_agent/adminbatchunfreezeagentcommissionlogic.go index 82d5cb1..63c5c52 100644 --- a/app/main/api/internal/logic/admin_agent/adminbatchunfreezeagentcommissionlogic.go +++ b/app/main/api/internal/logic/admin_agent/adminbatchunfreezeagentcommissionlogic.go @@ -57,7 +57,7 @@ func (l *AdminBatchUnfreezeAgentCommissionLogic) AdminBatchUnfreezeAgentCommissi // 计算总金额 var totalAmount float64 for _, commission := range commissions { - totalAmount += commission.Amount + totalAmount = roundMoney(totalAmount + commission.Amount) } // 开始事务 @@ -96,16 +96,16 @@ func (l *AdminBatchUnfreezeAgentCommissionLogic) AdminBatchUnfreezeAgentCommissi // 累加到对应代理商的钱包数据 if wallet, exists := agentWalletMap[commission.AgentId]; exists { - wallet.Balance += commission.Amount - wallet.FrozenBalance -= commission.Amount + wallet.Balance = roundMoney(wallet.Balance + commission.Amount) + wallet.FrozenBalance = roundMoney(wallet.FrozenBalance - commission.Amount) } else { // 查询该代理商的钱包 wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, commission.AgentId) if err != nil { return err } - wallet.Balance += commission.Amount - wallet.FrozenBalance -= commission.Amount + wallet.Balance = roundMoney(wallet.Balance + commission.Amount) + wallet.FrozenBalance = roundMoney(wallet.FrozenBalance - commission.Amount) agentWalletMap[commission.AgentId] = wallet } } diff --git a/app/main/api/internal/logic/admin_agent/adminreviewbankcardwithdrawallogic.go b/app/main/api/internal/logic/admin_agent/adminreviewbankcardwithdrawallogic.go index cd2b680..254ba08 100644 --- a/app/main/api/internal/logic/admin_agent/adminreviewbankcardwithdrawallogic.go +++ b/app/main/api/internal/logic/admin_agent/adminreviewbankcardwithdrawallogic.go @@ -1,11 +1,11 @@ package admin_agent import ( + "bdrp-server/app/main/model" + "bdrp-server/common/xerr" "context" "database/sql" "time" - "bdrp-server/app/main/model" - "bdrp-server/common/xerr" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/stores/sqlx" @@ -164,7 +164,7 @@ func (l *AdminReviewBankCardWithdrawalLogic) approveBankCardWithdrawal(ctx conte frozenBalanceBefore := wallet.FrozenBalance // 更新钱包(减少冻结余额) - wallet.FrozenBalance -= record.Amount + wallet.FrozenBalance = roundMoney(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) } @@ -175,14 +175,14 @@ func (l *AdminReviewBankCardWithdrawalLogic) approveBankCardWithdrawal(ctx conte session, wallet.AgentId, model.WalletTransactionTypeWithdraw, - -record.Amount, // 变动金额(负数表示减少) - wallet.Balance, // 变动前余额(不变) - wallet.Balance, // 变动后余额(不变) - frozenBalanceBefore, // 变动前冻结余额 - wallet.FrozenBalance, // 变动后冻结余额 - record.WithdrawNo, // 关联交易ID - 0, // 关联用户ID - "提现审核通过", // 备注 + roundMoney(-record.Amount), // 变动金额(负数表示减少) + roundMoney(wallet.Balance), // 变动前余额(不变) + roundMoney(wallet.Balance), // 变动后余额(不变) + roundMoney(frozenBalanceBefore), // 变动前冻结余额 + roundMoney(wallet.FrozenBalance), // 变动后冻结余额 + record.WithdrawNo, // 关联交易ID + 0, // 关联用户ID + "提现审核通过", // 备注 ) if err != nil { return err @@ -234,8 +234,8 @@ func (l *AdminReviewBankCardWithdrawalLogic) rejectWithdrawal(ctx context.Contex frozenBalanceBefore := wallet.FrozenBalance // 更新钱包(余额增加,冻结余额减少) - wallet.Balance += record.Amount - wallet.FrozenBalance -= record.Amount + wallet.Balance = roundMoney(wallet.Balance + record.Amount) + wallet.FrozenBalance = roundMoney(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) } @@ -246,14 +246,14 @@ func (l *AdminReviewBankCardWithdrawalLogic) rejectWithdrawal(ctx context.Contex session, wallet.AgentId, model.WalletTransactionTypeUnfreeze, - record.Amount, // 变动金额(正数表示增加) - balanceBefore, // 变动前余额 - wallet.Balance, // 变动后余额 - frozenBalanceBefore, // 变动前冻结余额 - wallet.FrozenBalance, // 变动后冻结余额 - record.WithdrawNo, // 关联交易ID - 0, // 关联用户ID - "提现拒绝,解冻资金", // 备注 + roundMoney(record.Amount), // 变动金额(正数表示增加) + roundMoney(balanceBefore), // 变动前余额 + roundMoney(wallet.Balance), // 变动后余额 + roundMoney(frozenBalanceBefore), // 变动前冻结余额 + roundMoney(wallet.FrozenBalance), // 变动后冻结余额 + record.WithdrawNo, // 关联交易ID + 0, // 关联用户ID + "提现拒绝,解冻资金", // 备注 ) if err != nil { return err @@ -295,7 +295,7 @@ func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalSuccess(ctx conte frozenBalanceBefore := wallet.FrozenBalance // 更新钱包(减少冻结余额) - wallet.FrozenBalance -= record.Amount + wallet.FrozenBalance = roundMoney(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) } @@ -306,14 +306,14 @@ func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalSuccess(ctx conte session, wallet.AgentId, model.WalletTransactionTypeWithdraw, - -record.Amount, // 变动金额(负数表示减少) - wallet.Balance, // 变动前余额(不变) - wallet.Balance, // 变动后余额(不变) - frozenBalanceBefore, // 变动前冻结余额 - wallet.FrozenBalance, // 变动后冻结余额 - record.WithdrawNo, // 关联交易ID - 0, // 关联用户ID - "提现成功", // 备注 + roundMoney(-record.Amount), // 变动金额(负数表示减少) + roundMoney(wallet.Balance), // 变动前余额(不变) + roundMoney(wallet.Balance), // 变动后余额(不变) + roundMoney(frozenBalanceBefore), // 变动前冻结余额 + roundMoney(wallet.FrozenBalance), // 变动后冻结余额 + record.WithdrawNo, // 关联交易ID + 0, // 关联用户ID + "提现成功", // 备注 ) if err != nil { return err @@ -365,8 +365,8 @@ func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalFailure(ctx conte frozenBalanceBefore := wallet.FrozenBalance // 更新钱包(余额增加,冻结余额减少) - wallet.Balance += record.Amount - wallet.FrozenBalance -= record.Amount + wallet.Balance = roundMoney(wallet.Balance + record.Amount) + wallet.FrozenBalance = roundMoney(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) } @@ -377,14 +377,14 @@ func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalFailure(ctx conte session, wallet.AgentId, model.WalletTransactionTypeUnfreeze, - record.Amount, // 变动金额(正数表示增加) - balanceBefore, // 变动前余额 - wallet.Balance, // 变动后余额 - frozenBalanceBefore, // 变动前冻结余额 - wallet.FrozenBalance, // 变动后冻结余额 - record.WithdrawNo, // 关联交易ID - 0, // 关联用户ID - "提现失败,解冻资金", // 备注 + roundMoney(record.Amount), // 变动金额(正数表示增加) + roundMoney(balanceBefore), // 变动前余额 + roundMoney(wallet.Balance), // 变动后余额 + roundMoney(frozenBalanceBefore), // 变动前冻结余额 + roundMoney(wallet.FrozenBalance), // 变动后冻结余额 + record.WithdrawNo, // 关联交易ID + 0, // 关联用户ID + "提现失败,解冻资金", // 备注 ) if err != nil { return err diff --git a/app/main/api/internal/logic/admin_agent/adminupdateagentwalletbalancelogic.go b/app/main/api/internal/logic/admin_agent/adminupdateagentwalletbalancelogic.go index 583339b..9ce3d22 100644 --- a/app/main/api/internal/logic/admin_agent/adminupdateagentwalletbalancelogic.go +++ b/app/main/api/internal/logic/admin_agent/adminupdateagentwalletbalancelogic.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math" "bdrp-server/app/main/api/internal/svc" "bdrp-server/app/main/api/internal/types" @@ -14,6 +15,10 @@ import ( "github.com/zeromicro/go-zero/core/stores/sqlx" ) +func roundMoney(v float64) float64 { + return math.Round(v*100) / 100 +} + type AdminUpdateAgentWalletBalanceLogic struct { logx.Logger ctx context.Context @@ -47,7 +52,8 @@ func (l *AdminUpdateAgentWalletBalanceLogic) AdminUpdateAgentWalletBalance(req * } // 计算新余额 - newBalance := wallet.Balance + req.Amount + adjustAmount := roundMoney(req.Amount) + newBalance := roundMoney(wallet.Balance + adjustAmount) // 校验余额不能为负数 if newBalance < 0 { @@ -68,20 +74,20 @@ func (l *AdminUpdateAgentWalletBalanceLogic) AdminUpdateAgentWalletBalance(req * } // 创建钱包交易流水记录(手动调整) - remark := fmt.Sprintf("管理员手动调整余额,金额: %.2f", req.Amount) + remark := fmt.Sprintf("管理员手动调整余额,金额: %.2f", adjustAmount) transErr := l.svcCtx.AgentService.CreateWalletTransaction( transCtx, session, req.AgentId, model.WalletTransactionTypeAdjust, - req.Amount, // 变动金额(正数表示增加,负数表示减少) - balanceBefore, // 变动前余额 - wallet.Balance, // 变动后余额 - frozenBalanceBefore, // 变动前冻结余额 - wallet.FrozenBalance, // 变动后冻结余额(保持不变) - "", // 关联交易ID(无关联) - 0, // 关联用户ID(无关联) - remark, // 备注 + adjustAmount, // 变动金额(正数表示增加,负数表示减少) + roundMoney(balanceBefore), // 变动前余额 + roundMoney(wallet.Balance), // 变动后余额 + roundMoney(frozenBalanceBefore), // 变动前冻结余额 + roundMoney(wallet.FrozenBalance), // 变动后冻结余额(保持不变) + "", // 关联交易ID(无关联) + 0, // 关联用户ID(无关联) + remark, // 备注 ) if transErr != nil { l.Logger.Errorf("创建代理钱包流水记录失败: %+v", transErr) @@ -89,7 +95,7 @@ func (l *AdminUpdateAgentWalletBalanceLogic) AdminUpdateAgentWalletBalance(req * } l.Logger.Infof("代理钱包余额变更 - AgentId: %d, 原余额: %.2f, 变更金额: %.2f, 新余额: %.2f", - req.AgentId, balanceBefore, req.Amount, wallet.Balance) + req.AgentId, roundMoney(balanceBefore), adjustAmount, roundMoney(wallet.Balance)) return nil }) @@ -101,7 +107,7 @@ func (l *AdminUpdateAgentWalletBalanceLogic) AdminUpdateAgentWalletBalance(req * resp = &types.AdminUpdateAgentWalletBalanceResp{ Success: true, - Balance: newBalance, + Balance: roundMoney(newBalance), } return } diff --git a/app/main/api/internal/logic/agent/agentwithdrawallogic.go b/app/main/api/internal/logic/agent/agentwithdrawallogic.go index e832927..76e2659 100644 --- a/app/main/api/internal/logic/agent/agentwithdrawallogic.go +++ b/app/main/api/internal/logic/agent/agentwithdrawallogic.go @@ -1,14 +1,14 @@ package agent import ( - "context" - "database/sql" - "fmt" - "time" "bdrp-server/app/main/model" "bdrp-server/common/ctxdata" "bdrp-server/common/xerr" "bdrp-server/pkg/lzkit/lzUtils" + "context" + "database/sql" + "fmt" + "time" "github.com/cenkalti/backoff/v4" "github.com/pkg/errors" @@ -56,6 +56,8 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types agentID int64 ) var finalWithdrawAmount float64 // 实际到账金额 + withdrawAmount := roundMoney(req.Amount) + // 使用事务处理核心操作 err := l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { userID, err := ctxdata.GetUidFromCtx(l.ctx) @@ -89,7 +91,7 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types } // 校验可提现金额 - if req.Amount > agentWallet.Balance { + if withdrawAmount > roundMoney(agentWallet.Balance) { return errors.Wrapf(xerr.NewErrMsg("您可提现的余额不足"), "获取用户ID失败") } @@ -97,7 +99,7 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types outBizNo = "W_" + l.svcCtx.AlipayService.GenerateOutTradeNo() // 冻结资金(事务内操作) - if err = l.freezeFunds(session, agentWallet, req.Amount); err != nil { + if err = l.freezeFunds(session, agentWallet, withdrawAmount); err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "资金冻结失败: %v", err) } yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month())) @@ -111,14 +113,14 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types ) // 统一扣税逻辑:所有提现都按6%收取税收 - exemptionAmount = 0 // 免税金额 = 0 - TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税 - taxDeductionPart = req.Amount // 应税金额 = 提现金额 - taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率 - finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费 + exemptionAmount = 0 // 免税金额 = 0 + TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税 + taxDeductionPart = withdrawAmount // 应税金额 = 提现金额 + taxAmount = roundMoney(taxDeductionPart * taxRate) // 应缴税费 = 应税金额 * 税率 + finalWithdrawAmount = roundMoney(withdrawAmount - taxAmount) // 实际到账金额 = 提现金额 - 应缴税费 // 创建提现记录(初始状态为处理中) - withdrawalID, err := l.createWithdrawalRecord(session, agentModel.Id, req.PayeeAccount, req.PayeeName, req.Amount, finalWithdrawAmount, taxAmount, outBizNo) + withdrawalID, err := l.createWithdrawalRecord(session, agentModel.Id, req.PayeeAccount, req.PayeeName, withdrawAmount, finalWithdrawAmount, taxAmount, outBizNo) if err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err) } @@ -127,7 +129,7 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types AgentId: agentModel.Id, YearMonth: yearMonth, WithdrawalId: withdrawalID, - WithdrawalAmount: req.Amount, + WithdrawalAmount: withdrawAmount, ExemptionAmount: exemptionAmount, TaxableAmount: taxDeductionPart, TaxRate: taxRate, @@ -153,7 +155,7 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types withdrawRes.Status = WithdrawStatusProcessing withdrawRes.FailMsg = "" - l.Logger.Infof("支付宝提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, req.Amount) + l.Logger.Infof("支付宝提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, withdrawAmount) return withdrawRes, nil } @@ -193,6 +195,7 @@ func (l *AgentWithdrawalLogic) mapAlipayError(code string) string { if msg, ok := errorMapping[code]; ok { return msg } + l.Logger.Infof("未匹配到支付宝错误码 code:%s", code) return "系统错误,请联系客服" } @@ -204,9 +207,9 @@ func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agen WithdrawNo: outBizNo, PayeeAccount: payeeAccount, PayeeName: sql.NullString{String: payeeName, Valid: true}, // 设置收款人姓名 - Amount: amount, - ActualAmount: finalWithdrawAmount, - TaxAmount: taxAmount, + Amount: roundMoney(amount), + ActualAmount: roundMoney(finalWithdrawAmount), + TaxAmount: roundMoney(taxAmount), Status: StatusProcessing, } @@ -219,8 +222,8 @@ func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agen // 冻结资金(事务内操作) func (l *AgentWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *model.AgentWallet, amount float64) error { - wallet.Balance -= amount - wallet.FrozenBalance += amount + wallet.Balance = roundMoney(wallet.Balance - amount) + wallet.FrozenBalance = roundMoney(wallet.FrozenBalance + amount) err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet) if err != nil { return err @@ -296,8 +299,8 @@ func (l *AgentWithdrawalLogic) updateWithdrawalStatus(outBizNo string, status in return err } - wallet.Balance += record.Amount - wallet.FrozenBalance -= record.Amount + wallet.Balance = roundMoney(wallet.Balance + record.Amount) + wallet.FrozenBalance = roundMoney(wallet.FrozenBalance - record.Amount) if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return err } @@ -320,7 +323,7 @@ func (l *AgentWithdrawalLogic) updateWithdrawalStatus(outBizNo string, status in return err } - wallet.FrozenBalance -= record.Amount + wallet.FrozenBalance = roundMoney(wallet.FrozenBalance - record.Amount) if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return err } diff --git a/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go b/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go index 0ff327b..ad8b529 100644 --- a/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go +++ b/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go @@ -1,13 +1,13 @@ package agent import ( + "bdrp-server/app/main/model" + "bdrp-server/common/ctxdata" + "bdrp-server/common/xerr" "context" "database/sql" "regexp" "time" - "bdrp-server/app/main/model" - "bdrp-server/common/ctxdata" - "bdrp-server/common/xerr" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/stores/sqlx" @@ -57,6 +57,8 @@ func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdraw return nil, errors.Wrapf(xerr.NewErrMsg("开户支行不能为空"), "开户支行验证失败") } + withdrawAmount := roundMoney(req.Amount) + // 使用事务处理核心操作 err = l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { userID, err := ctxdata.GetUidFromCtx(l.ctx) @@ -90,12 +92,12 @@ func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdraw } // 校验可提现金额 - if req.Amount > agentWallet.Balance { + if withdrawAmount > roundMoney(agentWallet.Balance) { return errors.Wrapf(xerr.NewErrMsg("您可提现的余额不足"), "余额不足") } // 最低提现金额验证 - if req.Amount < 50 { + if withdrawAmount < 50 { return errors.Wrapf(xerr.NewErrMsg("提现金额不能低于50元"), "金额验证失败") } @@ -103,7 +105,7 @@ func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdraw outBizNo = "BC_" + l.svcCtx.AlipayService.GenerateOutTradeNo() // 冻结资金(事务内操作) - if err = l.freezeFunds(session, agentWallet, req.Amount); err != nil { + if err = l.freezeFunds(session, agentWallet, withdrawAmount); err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "资金冻结失败: %v", err) } @@ -118,14 +120,14 @@ func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdraw ) // 统一扣税逻辑:所有提现都按6%收取税收 - exemptionAmount = 0 // 免税金额 = 0 - TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税 - taxDeductionPart = req.Amount // 应税金额 = 提现金额 - taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率 - finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费 + exemptionAmount = 0 // 免税金额 = 0 + TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税 + taxDeductionPart = withdrawAmount // 应税金额 = 提现金额 + taxAmount = roundMoney(taxDeductionPart * taxRate) // 应缴税费 = 应税金额 * 税率 + finalWithdrawAmount = roundMoney(withdrawAmount - taxAmount) // 实际到账金额 = 提现金额 - 应缴税费 // 创建提现记录(初始状态为申请中,提现类型为银行卡) - withdrawalID, err := l.createBankCardWithdrawalRecord(session, agentModel.Id, req.BankCardNo, req.BankName, agentRealName.Name, req.Amount, finalWithdrawAmount, taxAmount, outBizNo) + withdrawalID, err := l.createBankCardWithdrawalRecord(session, agentModel.Id, req.BankCardNo, req.BankName, agentRealName.Name, withdrawAmount, finalWithdrawAmount, taxAmount, outBizNo) if err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err) } @@ -135,7 +137,7 @@ func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdraw AgentId: agentModel.Id, YearMonth: yearMonth, WithdrawalId: withdrawalID, - WithdrawalAmount: req.Amount, + WithdrawalAmount: withdrawAmount, ExemptionAmount: exemptionAmount, TaxableAmount: taxDeductionPart, TaxRate: taxRate, @@ -160,7 +162,7 @@ func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdraw withdrawRes.Status = WithdrawStatusProcessing withdrawRes.FailMsg = "" - l.Logger.Infof("银行卡提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, req.Amount) + l.Logger.Infof("银行卡提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, withdrawAmount) return withdrawRes, nil } @@ -171,9 +173,9 @@ func (l *BankCardWithdrawalLogic) createBankCardWithdrawalRecord(session sqlx.Se WithdrawType: WithdrawTypeBankCard, // 银行卡提现 WithdrawNo: outBizNo, PayeeAccount: bankCardNo, // 银行卡号存储在PayeeAccount字段 - Amount: amount, - ActualAmount: finalWithdrawAmount, - TaxAmount: taxAmount, + Amount: roundMoney(amount), + ActualAmount: roundMoney(finalWithdrawAmount), + TaxAmount: roundMoney(taxAmount), Status: StatusProcessing, // 申请中状态 BankCardNo: sql.NullString{String: bankCardNo, Valid: true}, BankName: sql.NullString{String: bankName, Valid: true}, @@ -194,8 +196,8 @@ func (l *BankCardWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *mode frozenBalanceBefore := wallet.FrozenBalance // 更新钱包余额 - wallet.Balance -= amount - wallet.FrozenBalance += amount + wallet.Balance = roundMoney(wallet.Balance - amount) + wallet.FrozenBalance = roundMoney(wallet.FrozenBalance + amount) err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet) if err != nil { return err @@ -207,7 +209,7 @@ func (l *BankCardWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *mode session, wallet.AgentId, model.WalletTransactionTypeFreeze, - amount, // 变动金额 + roundMoney(amount), // 变动金额 balanceBefore, // 变动前余额 wallet.Balance, // 变动后余额 frozenBalanceBefore, // 变动前冻结余额 diff --git a/app/main/api/internal/logic/agent/getagentproductconfiglogic.go b/app/main/api/internal/logic/agent/getagentproductconfiglogic.go index 36ac812..0121dca 100644 --- a/app/main/api/internal/logic/agent/getagentproductconfiglogic.go +++ b/app/main/api/internal/logic/agent/getagentproductconfiglogic.go @@ -5,6 +5,7 @@ import ( "bdrp-server/app/main/model" "bdrp-server/common/ctxdata" "bdrp-server/common/xerr" + "math" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/mr" @@ -32,6 +33,10 @@ func NewGetAgentProductConfigLogic(ctx context.Context, svcCtx *svc.ServiceConte type AgentProductConfigResp struct { } +func roundMoney(v float64) float64 { + return math.Round(v*100) / 100 +} + func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentProductConfigResp, err error) { userID, err := ctxdata.GetUidFromCtx(l.ctx) if err != nil { @@ -81,10 +86,10 @@ func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentP return } agentProductConfig.ProductID = config.ProductId - agentProductConfig.CostPrice = agentProductConfigModel.CostPrice - agentProductConfig.PriceRangeMin = agentProductConfigModel.PriceRangeMin - agentProductConfig.PriceRangeMax = agentProductConfigModel.PriceRangeMax - agentProductConfig.PPricingStandard = agentProductConfigModel.PricingStandard + agentProductConfig.CostPrice = roundMoney(agentProductConfigModel.CostPrice) + agentProductConfig.PriceRangeMin = roundMoney(agentProductConfigModel.PriceRangeMin) + agentProductConfig.PriceRangeMax = roundMoney(agentProductConfigModel.PriceRangeMax) + agentProductConfig.PPricingStandard = roundMoney(agentProductConfigModel.PricingStandard) agentProductConfig.POverpricingRatio = agentProductConfigModel.OverpricingRatio // 看推广人是否有上级,上级是否有这个配置权限,上级是否有相关配置 @@ -119,10 +124,10 @@ func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentP cancel(membershipConfigErr) return } - agentProductConfig.CostPrice += membershipUserConfigModel.PriceIncreaseAmount - agentProductConfig.PriceRangeMin += membershipUserConfigModel.PriceIncreaseAmount - agentProductConfig.APricingStandard = membershipUserConfigModel.PriceRangeFrom - agentProductConfig.APricingEnd = membershipUserConfigModel.PriceRangeTo + agentProductConfig.CostPrice = roundMoney(agentProductConfig.CostPrice + membershipUserConfigModel.PriceIncreaseAmount) + agentProductConfig.PriceRangeMin = roundMoney(agentProductConfig.PriceRangeMin + membershipUserConfigModel.PriceIncreaseAmount) + agentProductConfig.APricingStandard = roundMoney(membershipUserConfigModel.PriceRangeFrom) + agentProductConfig.APricingEnd = roundMoney(membershipUserConfigModel.PriceRangeTo) agentProductConfig.AOverpricingRatio = membershipUserConfigModel.PriceRatio } } diff --git a/app/main/api/internal/logic/app/getappconfiglogic.go b/app/main/api/internal/logic/app/getappconfiglogic.go new file mode 100644 index 0000000..cc7b1d1 --- /dev/null +++ b/app/main/api/internal/logic/app/getappconfiglogic.go @@ -0,0 +1,51 @@ +package app + +import ( + "context" + "strconv" + + "bdrp-server/app/main/api/internal/svc" + "bdrp-server/app/main/api/internal/types" + "bdrp-server/app/main/model" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GetAppConfigLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGetAppConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAppConfigLogic { + return &GetAppConfigLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GetAppConfigLogic) GetAppConfig() (resp *types.GetAppConfigResp, err error) { + const defaultRetentionDays int64 = 30 + retentionDays := defaultRetentionDays + + retentionConfig, findErr := l.svcCtx.QueryCleanupConfigModel.FindOneByConfigKey(l.ctx, "retention_days") + if findErr != nil { + if findErr != model.ErrNotFound { + l.Errorf("获取 APP 配置 retention_days 失败: %+v", findErr) + } + } else if retentionConfig.Status == 1 { + parsedDays, parseErr := strconv.ParseInt(retentionConfig.ConfigValue, 10, 64) + if parseErr != nil || parsedDays <= 0 { + l.Errorf("解析 APP 配置 retention_days 失败, value=%s, err=%+v", retentionConfig.ConfigValue, parseErr) + } else { + retentionDays = parsedDays + } + } + + return &types.GetAppConfigResp{ + Query: types.AppQueryConfig{ + RetentionDays: retentionDays, + }, + }, nil +} diff --git a/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go b/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go index e77e417..82c1903 100644 --- a/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go +++ b/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go @@ -1,14 +1,15 @@ package pay import ( - "context" - "database/sql" - "net/http" - "strings" - "time" "bdrp-server/app/main/api/internal/svc" "bdrp-server/app/main/model" "bdrp-server/common/globalkey" + "context" + "database/sql" + "math" + "net/http" + "strings" + "time" "github.com/Masterminds/squirrel" "github.com/pkg/errors" @@ -17,10 +18,15 @@ import ( "github.com/zeromicro/go-zero/core/stores/sqlx" ) +func roundMoney(v float64) float64 { + return math.Round(v*100) / 100 +} + // HandleCommissionAndWalletDeduction 处理退款后的佣金状态更新和钱包金额扣除 // refundAmount 为本次实际退款金额(单位:元),从代理侧总共需要承担的金额 // 该函数会优先冲减当前订单相关的佣金(基于 RefundedAmount),不足部分再从钱包余额/冻结余额中扣除 func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.ServiceContext, session sqlx.Session, order *model.Order, refundAmount float64) error { + refundAmount = roundMoney(refundAmount) if refundAmount <= 0 { return nil } @@ -52,7 +58,7 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service // 1. 先在佣金记录上做冲减:增加 RefundedAmount,必要时将状态置为已退款 for _, commission := range commissions { - available := commission.Amount - commission.RefundedAmount + available := roundMoney(commission.Amount - commission.RefundedAmount) if available <= 0 { continue } @@ -66,11 +72,12 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service if currentRefund > remainRefundAmount { currentRefund = remainRefundAmount } + currentRefund = roundMoney(currentRefund) // 更新佣金的已退款金额 - commission.RefundedAmount += currentRefund + commission.RefundedAmount = roundMoney(commission.RefundedAmount + currentRefund) // 如果这条佣金已经被完全冲减,则标记为已退款 - if commission.RefundedAmount >= commission.Amount { + if commission.RefundedAmount >= roundMoney(commission.Amount) { commission.Status = 2 } @@ -92,9 +99,9 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service wa = &walletAdjust{agentId: commission.AgentId} walletAdjustMap[commission.AgentId] = wa } - wa.amount += currentRefund + wa.amount = roundMoney(wa.amount + currentRefund) - remainRefundAmount -= currentRefund + remainRefundAmount = roundMoney(remainRefundAmount - currentRefund) } // 2. 再按代理维度,从钱包(冻结余额/可用余额)中扣除对应金额 @@ -115,19 +122,19 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service frozenBalanceBefore := wallet.FrozenBalance // 优先从冻结余额中扣除(与原先“冻结佣金优先使用冻结余额”的设计一致) - deduct := wa.amount + deduct := roundMoney(wa.amount) if wallet.FrozenBalance >= deduct { - wallet.FrozenBalance -= deduct + wallet.FrozenBalance = roundMoney(wallet.FrozenBalance - deduct) } else { - remaining := deduct - wallet.FrozenBalance + remaining := roundMoney(deduct - wallet.FrozenBalance) wallet.FrozenBalance = 0 // 可用余额可以为负数,由业务承担风险 - wallet.Balance -= remaining + wallet.Balance = roundMoney(wallet.Balance - remaining) } // 变动后余额和冻结余额 - balanceAfter := wallet.Balance - frozenBalanceAfter := wallet.FrozenBalance + balanceAfter := roundMoney(wallet.Balance) + frozenBalanceAfter := roundMoney(wallet.FrozenBalance) // 更新钱包 var updateWalletErr error if session != nil { @@ -145,7 +152,7 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service session, wa.agentId, model.WalletTransactionTypeRefund, - -wa.amount*-1, // 钱包流水金额为负数 + roundMoney(-wa.amount*-1), // 钱包流水金额为负数 balanceBefore, balanceAfter, frozenBalanceBefore, @@ -348,7 +355,7 @@ func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWr var refundAmountYuan float64 if notification.Amount != nil && notification.Amount.Refund != nil { // 微信退款金额单位为分,这里转换为元 - refundAmountYuan = float64(*notification.Amount.Refund) / 100.0 + refundAmountYuan = roundMoney(float64(*notification.Amount.Refund) / 100.0) } // 4. 根据订单号前缀处理不同类型的订单 diff --git a/app/main/api/internal/service/alipayService.go b/app/main/api/internal/service/alipayService.go index e396e1f..8ebeff7 100644 --- a/app/main/api/internal/service/alipayService.go +++ b/app/main/api/internal/service/alipayService.go @@ -1,11 +1,11 @@ package service import ( - "context" - "crypto/rand" "bdrp-server/app/main/api/internal/config" "bdrp-server/app/main/model" "bdrp-server/pkg/lzkit/lzUtils" + "context" + "crypto/rand" "encoding/hex" "fmt" "net/http" @@ -217,12 +217,19 @@ func (a *AliPayService) AliTransfer( // 构造转账请求 req := alipay.FundTransUniTransfer{ - OutBizNo: outBizNo, - TransAmount: lzUtils.ToAlipayAmount(amount), // 金额格式转换 - ProductCode: "TRANS_ACCOUNT_NO_PWD", // 单笔无密转账到支付宝账户 - BizScene: "DIRECT_TRANSFER", // 单笔转账 - OrderTitle: "账户提现", // 转账标题 - Remark: remark, + OutBizNo: outBizNo, + TransAmount: lzUtils.ToAlipayAmount(amount), // 金额格式转换 + ProductCode: "TRANS_ACCOUNT_NO_PWD", // 单笔无密转账到支付宝账户 + BizScene: "DIRECT_TRANSFER", // 单笔转账 + OrderTitle: "账户提现", // 转账标题 + Remark: remark, + TransferSceneName: "业务结算", + TransferSceneReportInfo: []*alipay.TransferSceneReportInfo{ + { + InfoType: "结算款项名称", + InfoContent: "向代理商转账支付代理费用", + }, + }, PayeeInfo: &alipay.PayeeInfo{ Identity: payeeAccount, IdentityType: "ALIPAY_LOGON_ID", // 根据账户类型选择: diff --git a/app/main/api/internal/types/types.go b/app/main/api/internal/types/types.go index 136fccf..d3655df 100644 --- a/app/main/api/internal/types/types.go +++ b/app/main/api/internal/types/types.go @@ -1317,6 +1317,10 @@ type AgentWithdrawalListItem struct { PayeeName string `json:"payee_name"` // 收款人姓名 } +type AppQueryConfig struct { + RetentionDays int64 `json:"retention_days"` // 查询结果保留天数 +} + type AuthorizationDocumentInfo struct { DocumentId int64 `json:"documentId"` // 授权书ID UserId int64 `json:"userId"` // 用户ID @@ -1504,6 +1508,10 @@ type GetAgentSubordinateListResp struct { List []AgentSubordinateList `json:"list"` // 查询列表 } +type GetAppConfigResp struct { + Query AppQueryConfig `json:"query"` // 查询相关配置 +} + type GetAuthorizationDocumentByOrderReq struct { OrderId int64 `json:"orderId" validate:"required"` // 订单ID } diff --git a/go.mod b/go.mod index 03c749d..97134ee 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/samber/lo v1.50.0 github.com/shopspring/decimal v1.4.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - github.com/smartwalle/alipay/v3 v3.2.23 + github.com/smartwalle/alipay/v3 v3.2.29-0.20251230233706-e15a4853bc1c github.com/sony/sonyflake v1.2.0 github.com/stretchr/testify v1.11.1 github.com/tidwall/gjson v1.18.0 @@ -81,7 +81,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/smartwalle/ncrypto v1.0.4 // indirect - github.com/smartwalle/ngx v1.0.9 // indirect + github.com/smartwalle/ngx v1.0.12 // indirect github.com/smartwalle/nsign v1.0.9 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/cast v1.7.0 // indirect diff --git a/go.sum b/go.sum index 91cf61a..e3dbcc2 100644 --- a/go.sum +++ b/go.sum @@ -237,12 +237,12 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/smartwalle/alipay/v3 v3.2.23 h1:i1VwJeu70EmwpsXXz6GZZnMAtRx5MTfn2dPoql/L3zE= -github.com/smartwalle/alipay/v3 v3.2.23/go.mod h1:lVqFiupPf8YsAXaq5JXcwqnOUC2MCF+2/5vub+RlagE= +github.com/smartwalle/alipay/v3 v3.2.29-0.20251230233706-e15a4853bc1c h1:o48e92wOA4wuoWHtugNBU2wizp9ui4skQNcpqxcvgsM= +github.com/smartwalle/alipay/v3 v3.2.29-0.20251230233706-e15a4853bc1c/go.mod h1:dwPyjY5y17qUsDrsVYCQqmDBtQ/qpvY8JmQMvuJYGLQ= github.com/smartwalle/ncrypto v1.0.4 h1:P2rqQxDepJwgeO5ShoC+wGcK2wNJDmcdBOWAksuIgx8= github.com/smartwalle/ncrypto v1.0.4/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk= -github.com/smartwalle/ngx v1.0.9 h1:pUXDvWRZJIHVrCKA1uZ15YwNti+5P4GuJGbpJ4WvpMw= -github.com/smartwalle/ngx v1.0.9/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0= +github.com/smartwalle/ngx v1.0.12 h1:jcoCyu/0HtQ1y/gbiSLzqOUZcHnVLlKOmm0awRF7Mcg= +github.com/smartwalle/ngx v1.0.12/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0= github.com/smartwalle/nsign v1.0.9 h1:8poAgG7zBd8HkZy9RQDwasC6XZvJpDGQWSjzL2FZL6E= github.com/smartwalle/nsign v1.0.9/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=