Files

838 lines
28 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package gen
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2026 LiXiujie
// @Author Lxj <xxx@example.com>
// @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
})
}