838 lines
28 KiB
Go
838 lines
28 KiB
Go
// 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
|
||
})
|
||
}
|