// Package gen // @Link https://github.com/xiujiecn/xiu-admin // @Copyright Copyright (c) 2026 LiXiujie // @Author Lxj // @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE // @AutoGenerate Version package common import ( "context" "fmt" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchimport" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel" "strings" "time" "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/gctx" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" "github.com/google/uuid" ) type sCommonAgentBalance struct{} func NewCommonAgentBalance() *sCommonAgentBalance { return &sCommonAgentBalance{} } func init() { service.RegisterCommonAgentBalance(NewCommonAgentBalance()) } // Model 企业客户财务信息ORM模型 func (s *sCommonAgentBalance) Model(ctx context.Context, option ...*handler.Option) *gdb.Model { if len(option) == 0 { option = append(option, &handler.Option{ FilterTenant: true, FilterAuth: true, }) } return handler.Model(dao.TAgentBalance.Ctx(ctx), option...) } // List 获取企业客户财务信息列表 func (s *sCommonAgentBalance) List(ctx context.Context, in *model.TAgentBalanceListParam) (list []*model.TAgentBalanceListModel, totalCount int, err error) { mod := s.Model(ctx) deptId := contexts.GetDeptId(ctx) mod = mod.Fields(model.TAgentBalanceListModel{}) mod = mod.Where(dao.TAgentBalance.Columns().DeptId, deptId) if in.Id > 0 { mod = mod.Where(dao.TAgentBalance.Columns().Id, in.Id) } if in.WithdrawAmountStatus > 0 { mod = mod.Where(dao.TAgentBalance.Columns().WithdrawAmountStatus, in.WithdrawAmountStatus) } if in.Status > 0 { mod = mod.Where(dao.TAgentBalance.Columns().Status, in.Status) } if len(in.CreatedAt) == 2 { in.CreatedAt[1] = in.CreatedAt[1].EndOfDay() } if len(in.CreatedAt) == 2 { mod = mod.WhereBetween(dao.TAgentBalance.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1]) } mod = mod.Page(in.Page, in.PageSize) mod = mod.OrderDesc(dao.TAgentBalance.Columns().Id) mod = mod.Hook(hook.MemberSummary) if err = mod.ScanAndCount(&list, &totalCount, false); err != nil { err = gerror.Wrap(err, "获取企业客户财务信息列表失败,请稍后重试!") return } return } // Export 导出企业客户财务信息 func (s *sCommonAgentBalance) Export(ctx context.Context, in *model.TAgentBalanceListParam) (err error) { list, totalCount, err := s.List(ctx, in) if err != nil { return } tags, err := convert.GetEntityDescTags(model.TAgentBalanceExportModel{}) if err != nil { return } var ( fileName = "导出企业客户财务信息-" + gctx.CtxId(ctx) sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, (totalCount+in.PageSize-1)/in.PageSize, in.Page, len(list)) exports []model.TAgentBalanceExportModel ) if err = gconv.Scan(list, &exports); err != nil { return } err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName) return } // Edit 修改/新增企业客户财务信息 func (s *sCommonAgentBalance) Edit(ctx context.Context, in *model.TAgentBalanceEditParam) (err error) { return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { if in.Id > 0 { updatedBy := contexts.GetUserId(ctx) in.UpdatedBy = &updatedBy in.UpdatedAt = gtime.Now() if _, err = s.Model(ctx). Fields(model.TAgentBalanceUpdateFields{}). WherePri(in.Id).Data(in).Update(); err != nil { err = gerror.Wrap(err, "修改企业客户财务信息失败,请稍后重试!") } return } tenantId := contexts.GetTenantId(ctx) in.TenantId = &tenantId createdDept := contexts.GetDeptId(ctx) in.CreatedDept = &createdDept createdBy := contexts.GetUserId(ctx) in.CreatedBy = &createdBy in.CreatedAt = gtime.Now() if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}). Fields(model.TAgentBalanceInsertFields{}). Data(in).Insert(); err != nil { err = gerror.Wrap(err, "新增企业客户财务信息失败,请稍后重试!") } return }) } // Delete 删除企业客户财务信息 func (s *sCommonAgentBalance) Delete(ctx context.Context, in *model.TAgentBalanceDeleteParam) (err error) { if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil { err = gerror.Wrap(err, "删除企业客户财务信息失败,请稍后重试!") return } return } // View 获取企业客户财务信息指定信息 func (s *sCommonAgentBalance) View(ctx context.Context, in *model.TAgentBalanceViewParam) (res *model.TAgentBalanceViewModel, err error) { if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil { err = gerror.Wrap(err, "获取企业客户财务信息信息,请稍后重试!") return } return } // Status 更新企业客户财务信息状态 func (s *sCommonAgentBalance) Status(ctx context.Context, in *model.TAgentBalanceStatusParam) (err error) { if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{ dao.TAgentBalance.Columns().Status: in.Status, dao.TAgentBalance.Columns().UpdatedBy: contexts.GetUserId(ctx), }).Update(); err != nil { err = gerror.Wrap(err, "更新企业客户财务信息状态失败,请稍后重试!") return } return } // GetAccountBalance 获取当前登录企业的账户余额信息。 func (s *sCommonAgentBalance) GetAccountBalance(ctx context.Context, in *model.TAgentBalanceTrendParam) (res *model.TAgentBalanceChartData, err error) { res = &model.TAgentBalanceChartData{} deptId := contexts.GetDeptId(ctx) months, startTime, endTime, err := agentBalanceMonthRange(in.StartMonth, in.EndMonth) if err != nil { return nil, err } balanceMap, err := s.getMonthlyLastBalance(ctx, dao.TBalanceDetail.Ctx(ctx), dao.TBalanceDetail.Columns().TransactionTime, dao.TBalanceDetail.Columns().BalanceAfter, dao.TBalanceDetail.Columns().DeptId, deptId, startTime, endTime, "余额趋势") if err != nil { return nil, err } withdrawableMap, err := s.getMonthlyLastBalance(ctx, dao.TFinWithdrawableChange.Ctx(ctx), dao.TFinWithdrawableChange.Columns().CreatedAt, dao.TFinWithdrawableChange.Columns().AfterBalance, dao.TFinWithdrawableChange.Columns().DeptId, deptId, startTime, endTime, "可提现金额趋势") if err != nil { return nil, err } creditMap, err := s.getMonthlyLastBalance(ctx, dao.TCreditDetail.Ctx(ctx), dao.TCreditDetail.Columns().TransactionTime, dao.TCreditDetail.Columns().CreditBalanceAfter, dao.TCreditDetail.Columns().DeptId, deptId, startTime, endTime, "信用额度趋势") if err != nil { return nil, err } balanceValues := make([]float64, 0, len(months)) withdrawableValues := make([]float64, 0, len(months)) creditValues := make([]float64, 0, len(months)) for _, month := range months { balanceValues = append(balanceValues, balanceMap[month]) withdrawableValues = append(withdrawableValues, withdrawableMap[month]) creditValues = append(creditValues, creditMap[month]) } res.Labels = months res.Series = []*model.TAgentBalanceChartSeries{ {Name: "余额", Values: balanceValues}, {Name: "可提现金额", Values: withdrawableValues}, {Name: "信用额度", Values: creditValues}, } return } // getMonthlyLastBalance 查询指定月份范围内每月最后一条记录的余额。 func (s *sCommonAgentBalance) getMonthlyLastBalance(ctx context.Context, mod *gdb.Model, timeColumn string, balanceColumn string, deptIdColumn string, deptId interface{}, startTime, endTime time.Time, errMsg string) (map[string]float64, error) { type maxTimeRow struct { AccountPeriod string `orm:"account_period"` MaxTime string `orm:"max_time"` } var maxTimes []*maxTimeRow if err := mod.Fields("DATE_FORMAT("+timeColumn+", '%Y-%m') as account_period, MAX("+timeColumn+") as max_time"). Where(deptIdColumn, deptId). WhereGTE(timeColumn, startTime). WhereLT(timeColumn, endTime.AddDate(0, 1, 0)). Group("DATE_FORMAT(" + timeColumn + ", '%Y-%m')"). Scan(&maxTimes); err != nil { return nil, gerror.Wrap(err, "查询"+errMsg+"失败,请稍后重试!") } result := make(map[string]float64) if len(maxTimes) == 0 { return result, nil } maxTimeList := make([]string, len(maxTimes)) for i, mt := range maxTimes { maxTimeList[i] = mt.MaxTime } type balanceRow struct { AccountPeriod string `orm:"account_period"` BalanceAfter float64 `orm:"balance_after"` } var allResults []*balanceRow if err := mod.Fields("DATE_FORMAT("+timeColumn+", '%Y-%m') as account_period, "+balanceColumn+" as balance_after"). Where(deptIdColumn, deptId). WhereIn(timeColumn, maxTimeList). Order(timeColumn + " ASC"). Scan(&allResults); err != nil { return nil, gerror.Wrap(err, "查询"+errMsg+"失败,请稍后重试!") } for _, item := range allResults { result[item.AccountPeriod] = item.BalanceAfter } return result, nil } // AccountTrend 获取收入金额、支出金额折线图。 func (s *sCommonAgentBalance) AccountTrend(ctx context.Context, in *model.TAgentBalanceTrendParam) (res *model.TAgentBalanceChartData, err error) { months, startMonth, endMonth, err := agentBalanceMonthRange(in.StartMonth, in.EndMonth) if err != nil { return nil, err } deptId := contexts.GetDeptId(ctx) type billRow struct { AccountPeriod string `orm:"account_period"` TotalIncome float64 `orm:"total_income"` TotalExpense float64 `orm:"total_expense"` } var bills []*billRow accountPeriodColumn := dao.TProfitAccountMonthBill.Columns().AccountPeriod deptIdColumn := dao.TProfitAccountMonthBill.Columns().DeptId totalIncomeColumn := dao.TProfitAccountMonthBill.Columns().TotalIncome totalExpenseColumn := dao.TProfitAccountMonthBill.Columns().TotalExpense mod := handler.Model(dao.TProfitAccountMonthBill.Ctx(ctx)) if in.AccountType == "2" { accountPeriodColumn = dao.TConsumeAccountMonthBill.Columns().AccountPeriod deptIdColumn = dao.TConsumeAccountMonthBill.Columns().DeptId totalIncomeColumn = dao.TConsumeAccountMonthBill.Columns().TotalIncome totalExpenseColumn = dao.TConsumeAccountMonthBill.Columns().TotalExpense mod = handler.Model(dao.TConsumeAccountMonthBill.Ctx(ctx)) } if err = mod.Fields(accountPeriodColumn, totalIncomeColumn, totalExpenseColumn). Where(deptIdColumn, deptId). WhereBetween(accountPeriodColumn, startMonth, endMonth). Scan(&bills); err != nil { return nil, gerror.Wrap(err, "查询账户总览趋势失败,请稍后重试!") } billMap := make(map[string]*billRow, len(bills)) for _, item := range bills { billMap[item.AccountPeriod] = item } incomeValues := make([]float64, 0, len(months)) expenseValues := make([]float64, 0, len(months)) for _, month := range months { if item, ok := billMap[month]; ok { incomeValues = append(incomeValues, item.TotalIncome) expenseValues = append(expenseValues, item.TotalExpense) continue } incomeValues = append(incomeValues, 0) expenseValues = append(expenseValues, 0) } return &model.TAgentBalanceChartData{ Labels: months, Series: []*model.TAgentBalanceChartSeries{ {Name: "收入金额", Values: incomeValues}, {Name: "支出金额", Values: expenseValues}, }, }, nil } // agentBalanceMonthRange 返回月份范围。 func agentBalanceMonthRange(startMonth, endMonth string) (months []string, startTime time.Time, endTime time.Time, err error) { now := time.Now() if endMonth == "" { endMonth = now.Format("2006-01") } if startMonth == "" { tempEndTime, parseErr := time.Parse("2006-01", endMonth) if parseErr != nil { err = gerror.New("结束月份格式不正确,应为YYYY-MM") return } startMonth = tempEndTime.AddDate(0, -11, 0).Format("2006-01") } startTime, err = time.Parse("2006-01", startMonth) if err != nil { err = gerror.New("开始月份格式不正确,应为YYYY-MM") return } endTime, err = time.Parse("2006-01", endMonth) if err != nil { err = gerror.New("结束月份格式不正确,应为YYYY-MM") return } if startTime.After(endTime) { err = gerror.New("开始月份不能大于结束月份") return } for t := startTime; !t.After(endTime); t = t.AddDate(0, 1, 0) { months = append(months, t.Format("2006-01")) } return months, startTime, endTime, nil } const ( financeIncomeExpenseIncome = 0 financeIncomeExpenseExpense = 1 financeBalanceTransactionTypeTransferToWithdraw = 2 financeBalanceTransactionTypeManualAdjust = 4 financeBalanceTransactionChannelBalance = 3 financeWithdrawableChangeTypeCommission = 0 financeWithdrawableChangeTypeBalanceTransferIn = 3 financeApplyTypeBalanceToWithdraw = 2 financeAuditTypeFirst = 1 financeApplyStatusPending = 0 financeApplyStatusApproved = 1 financeApplyStatusRejected = 2 financeAuditResultPass = 0 financeAuditResultReject = 1 financeAccountStatusNormal = 0 financeWithdrawAmountStatusNormal = 0 financeCommissionStatusPending = 0 financeCommissionStatusSettled = 1 ) // BalanceTransferApply 发起余额转可提现申请。申请时先扣余额并写余额明细,申请单保持待审核。 func (s *sCommonAgentBalance) BalanceTransferApply(ctx context.Context, in *model.TAgentBalanceTransferApplyParam) (*model.TAgentBalanceTransferApplyModel, error) { if in == nil { in = &model.TAgentBalanceTransferApplyParam{} } if err := in.Filter(ctx); err != nil { return nil, err } applyNo := "BWT" + strings.ReplaceAll(uuid.New().String(), "-", "") deptId := contexts.GetDeptId(ctx) tenantId := contexts.GetTenantId(ctx) userId := contexts.GetUserId(ctx) now := gtime.Now() res := &model.TAgentBalanceTransferApplyModel{ ApplyNo: applyNo, ApplyAmount: in.Amount, } err := g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { account := &entity.TAgentBalance{} columns := dao.TAgentBalance.Columns() if err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()). Where(columns.DeptId, deptId). Where(columns.TenantId, tenantId). Scan(account); err != nil { return gerror.Wrap(err, "查询企业客户财务账户失败,请稍后重试!") } if account.Id == 0 { return gerror.New("企业客户财务账户不存在") } if account.Status != financeAccountStatusNormal { return gerror.New("企业客户财务账户状态异常") } if account.WithdrawAmountStatus != financeWithdrawAmountStatusNormal { return gerror.New("可提现金额已冻结") } if account.Balance < in.Amount { return gerror.New("账户余额不足") } balanceAfter := account.Balance - in.Amount result, err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()). WherePri(account.Id). Data(g.Map{ columns.Balance: balanceAfter, columns.UpdatedBy: userId, columns.UpdatedAt: now, }). Update() if err != nil { return gerror.Wrap(err, "扣减账户余额失败,请稍后重试!") } affected, err := result.RowsAffected() if err != nil { return gerror.Wrap(err, "获取账户余额更新结果失败") } if affected == 0 { return gerror.New("账户余额未更新,请稍后重试") } if err = service.CommonTBalanceDetail().CreateBalanceTransferDetail(ctx, tx, &model.TBalanceTransferDetailCreateParam{ DeptId: deptId, SerialNo: applyNo, TransactionTime: now, IncomeExpenseType: financeIncomeExpenseExpense, TransactionType: financeBalanceTransactionTypeTransferToWithdraw, TransactionChannel: financeBalanceTransactionChannelBalance, Amount: in.Amount, BeforeBalance: account.Balance, BalanceAfter: balanceAfter, OrderSn: applyNo, Remark: "余额转可提现申请扣减余额", TenantId: tenantId, CreatedDept: deptId, CreatedBy: userId, CreatedAt: now, }); err != nil { return err } var topDept entity.SysDept if err := dao.SysDept.Ctx(ctx). Fields(dao.SysDept.Columns().DeptId, dao.SysDept.Columns().ParentId). Where(dao.SysDept.Columns().ParentId, 0). Scan(&topDept); err != nil { return gerror.Wrap(err, "查询审核机构失败,请稍后重试!") } if topDept.DeptId == 0 { return gerror.New("审核机构不存在") } if err = service.CommonTFinanceApply().CreateBalanceTransferApply(ctx, tx, &model.TFinanceApplyCreateBalanceTransferParam{ ApplyNo: applyNo, ApplyDeptId: deptId, AuditDeptId: topDept.DeptId, AvailableAmountSnapshot: account.WithdrawAmount, ApplyAmount: in.Amount, Remark: in.Remark, TenantId: tenantId, CreatedDept: deptId, CreatedBy: userId, CreatedAt: now, }); err != nil { return err } res.BalanceAfter = balanceAfter return nil }) if err != nil { return nil, gerror.Wrap(err, "余额转可提现申请失败,请稍后重试!") } return res, nil } // BalanceTransferAudit 审核余额转可提现申请。通过时加可提现金额,驳回时退回申请时扣掉的余额。 func (s *sCommonAgentBalance) BalanceTransferAudit(ctx context.Context, in *model.TAgentBalanceTransferAuditParam) error { if in == nil { in = &model.TAgentBalanceTransferAuditParam{} } if err := in.Filter(ctx); err != nil { return err } auditNo, err := batchimport.NextRandomNo("BTA") if err != nil { return gerror.Wrap(err, "生成审核单号失败") } tenantId := contexts.GetTenantId(ctx) currentDeptId := contexts.GetDeptId(ctx) currentUserId := contexts.GetUserId(ctx) if tenantId == "" { return gerror.New("当前租户不能为空") } if currentDeptId <= 0 { return gerror.New("当前登录机构不能为空") } now := gtime.Now() return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { apply, err := service.CommonTFinanceApply().GetPendingByApplyNo(ctx, tx, in.ApplyNo, tenantId) if err != nil { return err } if err = service.CommonTFinanceAuditRecord().CreateRecord(ctx, tx, &model.TFinanceAuditRecordCreateParam{ AuditNo: auditNo, ApplyNo: apply.ApplyNo, AuditDeptId: currentDeptId, AuditorUserId: currentUserId, AuditResult: in.AuditResult, AuditOpinion: in.AuditOpinion, AuditTime: now, }); err != nil { return err } applyStatus := financeApplyStatusApproved if in.AuditResult == financeAuditResultReject { applyStatus = financeApplyStatusRejected } if err = service.CommonTFinanceApply().UpdateAuditStatus(ctx, tx, &model.TFinanceApplyUpdateAuditStatusParam{ ApplyNo: apply.ApplyNo, ApplyStatus: applyStatus, AuditTime: now, UpdatedBy: currentUserId, UpdatedAt: now, }); err != nil { return err } account := &entity.TAgentBalance{} columns := dao.TAgentBalance.Columns() if err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()). Where(columns.DeptId, apply.ApplyDeptId). Where(columns.TenantId, apply.TenantId). Scan(account); err != nil { return gerror.Wrap(err, "查询企业客户财务账户失败,请稍后重试!") } if account.Id == 0 { return gerror.New("企业客户财务账户不存在") } if account.Status != financeAccountStatusNormal { return gerror.New("企业客户财务账户状态异常") } if in.AuditResult == financeAuditResultPass { if account.WithdrawAmountStatus != financeWithdrawAmountStatusNormal { return gerror.New("可提现金额已冻结") } before := account.WithdrawAmount after := before + apply.ApplyAmount result, err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()). WherePri(account.Id). Data(g.Map{ columns.WithdrawAmount: after, columns.UpdatedBy: currentUserId, columns.UpdatedAt: now, }). Update() if err != nil { return gerror.Wrap(err, "增加可提现金额失败,请稍后重试!") } affected, err := result.RowsAffected() if err != nil { return gerror.Wrap(err, "获取账户余额更新结果失败") } if affected == 0 { return gerror.New("账户余额未更新,请稍后重试") } return service.CommonTFinWithdrawableChange().CreateBalanceTransferInChange(ctx, tx, &model.TFinWithdrawableBalanceTransferInParam{ DeptId: apply.ApplyDeptId, ChangeAmount: apply.ApplyAmount, BeforeBalance: before, AfterBalance: after, AccountType: apply.AccountType, BankName: apply.BankName, BankAccount: apply.BankAccount, AccountName: apply.AccountName, IdentityCard: apply.IdentityCard, SourceNo: apply.ApplyNo, Remark: "余额转可提现审核通过", AccountPeriod: now.Format("Y-m"), TenantId: apply.TenantId, CreatedDept: apply.ApplyDeptId, CreatedBy: currentUserId, CreatedAt: now, }) } before := account.Balance after := before + apply.ApplyAmount result, err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()). WherePri(account.Id). Data(g.Map{ columns.Balance: after, columns.UpdatedBy: currentUserId, columns.UpdatedAt: now, }). Update() if err != nil { return gerror.Wrap(err, "退回账户余额失败,请稍后重试!") } affected, err := result.RowsAffected() if err != nil { return gerror.Wrap(err, "获取账户余额更新结果失败") } if affected == 0 { return gerror.New("账户余额未更新,请稍后重试") } remark := "余额转可提现申请驳回退款" if in.AuditOpinion != "" { remark += ":" + in.AuditOpinion } return service.CommonTBalanceDetail().CreateBalanceTransferDetail(ctx, tx, &model.TBalanceTransferDetailCreateParam{ DeptId: apply.ApplyDeptId, SerialNo: apply.ApplyNo, TransactionTime: now, IncomeExpenseType: financeIncomeExpenseIncome, TransactionType: financeBalanceTransactionTypeManualAdjust, TransactionChannel: financeBalanceTransactionChannelBalance, Amount: apply.ApplyAmount, BeforeBalance: before, BalanceAfter: after, OrderSn: apply.ApplyNo, Remark: remark, TenantId: apply.TenantId, CreatedDept: apply.ApplyDeptId, CreatedBy: currentUserId, CreatedAt: now, }) }) } // SettleCommissionByPeriod 按账期区间结算待结算分润,同步到企业账户可提现金额并写入可提现明细。 func (s *sCommonAgentBalance) SettleCommissionByPeriod(ctx context.Context, in *model.TAgentBalanceCommissionSettlementParam) (*model.TAgentBalanceCommissionSettlementResult, error) { if in == nil { in = &model.TAgentBalanceCommissionSettlementParam{} } if err := in.Filter(ctx); err != nil { return nil, err } res := &model.TAgentBalanceCommissionSettlementResult{ StartPeriod: in.StartPeriod, EndPeriod: in.EndPeriod, } endPeriodTime, _ := time.Parse("2006-01", in.EndPeriod) endNextPeriod := endPeriodTime.AddDate(0, 1, 0).Format("2006-01") var lastId int64 tenantId := contexts.GetTenantId(ctx) commissionColumns := dao.TFinCommissionDetail.Columns() for { var details []*entity.TFinCommissionDetail mod := handler.Model(dao.TFinCommissionDetail.Ctx(ctx), &handler.Option{ FilterTenant: false, FilterAuth: false, }). Fields( commissionColumns.Id, commissionColumns.BenefitDeptId, commissionColumns.CommissionAmount, commissionColumns.AccountPeriod, commissionColumns.TenantId, commissionColumns.CreatedDept, ). Where(commissionColumns.CommissionStatus, financeCommissionStatusPending). WhereGTE(commissionColumns.AccountPeriod, in.StartPeriod). WhereLT(commissionColumns.AccountPeriod, endNextPeriod). WhereGT(commissionColumns.Id, lastId). OrderAsc(commissionColumns.Id). Limit(1000) if tenantId != "" { mod = mod.Where(commissionColumns.TenantId, tenantId) } if err := mod.Scan(&details); err != nil { return res, gerror.Wrap(err, "扫描待结算分润明细失败,请稍后重试!") } if len(details) == 0 { break } res.ScanCount += len(details) for _, item := range details { if item.Id > lastId { lastId = item.Id } } groups := make(map[model.TAgentBalanceCommissionSettlementGroupKey]*model.TAgentBalanceCommissionSettlementGroup) for _, item := range details { if item.BenefitDeptId <= 0 || item.AccountPeriod == "" || item.CommissionAmount <= 0 { continue } key := model.TAgentBalanceCommissionSettlementGroupKey{ DeptId: item.BenefitDeptId, AccountPeriod: item.AccountPeriod, TenantId: item.TenantId, } group := groups[key] if group == nil { group = &model.TAgentBalanceCommissionSettlementGroup{ DeptId: item.BenefitDeptId, AccountPeriod: item.AccountPeriod, TenantId: item.TenantId, CreatedDept: item.CreatedDept, } groups[key] = group } group.Ids = append(group.Ids, item.Id) group.Amount += item.CommissionAmount } for _, group := range groups { if err := s.settleCommissionGroup(ctx, group); err != nil { res.FailGroupCount++ g.Log().Errorf(ctx, "分润结算失败 deptId:%d accountPeriod:%s amount:%v err:%+v", group.DeptId, group.AccountPeriod, group.Amount, err) continue } res.SuccessGroupCount++ res.SettledCount += len(group.Ids) res.SettledAmount += group.Amount } } return res, nil } func (s *sCommonAgentBalance) settleCommissionGroup(ctx context.Context, group *model.TAgentBalanceCommissionSettlementGroup) error { if group == nil || len(group.Ids) == 0 { return nil } now := gtime.Now() userId := contexts.GetUserId(ctx) accountColumns := dao.TAgentBalance.Columns() return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { account := &entity.TAgentBalance{} if err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()). Where(accountColumns.DeptId, group.DeptId). Where(accountColumns.TenantId, group.TenantId). LockUpdate(). Scan(account); err != nil { return gerror.Wrap(err, "锁定企业客户财务账户失败,请稍后重试!") } if account.Id == 0 { return gerror.New("企业客户财务账户不存在") } if account.Status != financeAccountStatusNormal { return gerror.New("企业客户财务账户状态异常") } if account.WithdrawAmountStatus != financeWithdrawAmountStatusNormal { return gerror.New("可提现金额已冻结") } if err := service.CommonTFinCommissionDetail().MarkSettled(ctx, tx, &model.TFinCommissionSettleUpdateParam{ Ids: group.Ids, SettlementTime: now, }); err != nil { return err } before := account.WithdrawAmount after := before + group.Amount sourceNo := fmt.Sprintf("CMS%sD%d%s", strings.ReplaceAll(group.AccountPeriod, "-", ""), group.DeptId, now.Format("150405")) if err := service.CommonTFinWithdrawableChange().CreateCommissionSettlementChange(ctx, tx, &model.TFinWithdrawableCommissionSettlementParam{ DeptId: group.DeptId, ChangeAmount: group.Amount, BeforeBalance: before, AfterBalance: after, SourceNo: sourceNo, Remark: "分润结算转可提现", AccountPeriod: group.AccountPeriod, TenantId: group.TenantId, CreatedDept: group.CreatedDept, CreatedBy: userId, CreatedAt: now, }); err != nil { return err } result, err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()). WherePri(account.Id). Data(g.Map{ accountColumns.WithdrawAmount: after, accountColumns.UpdatedBy: userId, accountColumns.UpdatedAt: now, }). Update() if err != nil { return gerror.Wrap(err, "更新企业客户可提现金额失败,请稍后重试!") } affected, err := result.RowsAffected() if err != nil { return gerror.Wrap(err, "获取企业客户可提现金额更新结果失败") } if affected == 0 { return gerror.New("企业客户可提现金额未更新,请稍后重试") } return nil }) }