chore: 初始开源发布
iotlcm-admin-svr:管理后台服务。 - Go module:iotlcm → git.5iot.com/iotlcm-public/iotlcm-admin-svr(729 文件) - 凭据与地址脱敏:JWT signingKey(含代码里的 MustGet 默认值)、 MySQL DSN、Redis 口令、告警邮件发件人邮箱全部改为占位; 私网 IP → 文档地址段;库名 xiujie_admin → iotlcm - 真实手机号/邮箱 → 合法 11 位编造号 / example.com - 移除内部资产:CI 触发器、过时 DDL(sql/db_iotlcm.sql)、 私有依赖 go get 行、明文 registry 登录口令 - 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名 - Dockerfile 改用公共基础镜像 - 拼错的 RADEME.md → README.md,README 重写 - 需要真实数据库的用例改为可用 IOTLCM_TEST_WITH_DB=1 显式启用 - 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 数据库 schema 不随本仓发布(生产 DDL 已过时),README 已说明。
This commit is contained in:
@@ -0,0 +1,637 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/allocationunits"
|
||||
"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"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type sCommonBatchExecutionLog struct{}
|
||||
|
||||
type batchExecutionLogSummaryBatch struct {
|
||||
CreatedDept int64 `orm:"created_dept"`
|
||||
TargetDeptId int64 `orm:"target_dept_id"`
|
||||
SourceChannel string `orm:"source_channel"`
|
||||
ExecutionMode int `orm:"execution_mode"`
|
||||
Id int64 `orm:"id"`
|
||||
BatchNo string `orm:"batch_no"`
|
||||
BatchType int `orm:"batch_type"`
|
||||
SourceBatchNo string `orm:"source_batch_no"`
|
||||
SourceType *int `orm:"source_type"`
|
||||
BatchAsyncStatus int `orm:"batch_async_status"`
|
||||
RunResult int `orm:"run_result"`
|
||||
FailReason string `orm:"fail_reason"`
|
||||
TotalCount int `orm:"total_count"`
|
||||
SourceOssId int64 `orm:"source_oss_id"`
|
||||
ResultOssId int64 `orm:"result_oss_id"`
|
||||
BatchInfo *gjson.Json `orm:"batch_info"`
|
||||
StartTime *gtime.Time `orm:"start_time"`
|
||||
EndTime *gtime.Time `orm:"end_time"`
|
||||
}
|
||||
|
||||
func NewCommonBatchExecutionLog() *sCommonBatchExecutionLog {
|
||||
return &sCommonBatchExecutionLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonBatchExecutionLog(NewCommonBatchExecutionLog())
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) batchModel(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.BatchMain.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) detailModel(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.BatchDetail.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) downloadModel(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: false,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.TResourceBatchDownload.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) ossModel(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: false,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.SysOss.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) List(ctx context.Context, in *model.BatchExecutionLogListParam) (list []*model.BatchExecutionLogListModel, totalCount int, err error) {
|
||||
mod := s.batchModel(ctx).
|
||||
Fields(model.BatchExecutionLogListModel{})
|
||||
|
||||
if in.BatchNo != "" {
|
||||
mod = mod.Where(dao.BatchMain.Columns().BatchNo+" LIKE ?", "%"+in.BatchNo+"%")
|
||||
}
|
||||
if keyword := strings.TrimSpace(in.Keyword); keyword != "" {
|
||||
like := "%" + keyword + "%"
|
||||
mod = mod.Where(
|
||||
"("+dao.BatchMain.Columns().BatchNo+" LIKE ? OR "+dao.BatchMain.Columns().OrderSn+" LIKE ? OR "+dao.BatchMain.Columns().FailReason+" LIKE ?)",
|
||||
like,
|
||||
like,
|
||||
like,
|
||||
)
|
||||
}
|
||||
if in.BatchAsyncStatus != nil {
|
||||
mod = mod.Where(dao.BatchMain.Columns().BatchAsyncStatus, *in.BatchAsyncStatus)
|
||||
}
|
||||
if in.SourceType != nil {
|
||||
mod = mod.Where(dao.BatchMain.Columns().SourceType, *in.SourceType)
|
||||
}
|
||||
if in.SourceChannel != "" {
|
||||
mod = mod.Where(dao.BatchMain.Columns().SourceChannel, in.SourceChannel)
|
||||
}
|
||||
if in.ExecutionMode != nil {
|
||||
mod = mod.Where(dao.BatchMain.Columns().ExecutionMode, *in.ExecutionMode)
|
||||
}
|
||||
mod = mod.Where(
|
||||
fmt.Sprintf("NOT (%s = ? AND COALESCE(%s, '') <> '' AND %s <> %s)", dao.BatchMain.Columns().BatchType, dao.BatchMain.Columns().SourceBatchNo, dao.BatchMain.Columns().SourceBatchNo, dao.BatchMain.Columns().BatchNo),
|
||||
consts.BatchTypeAllocate,
|
||||
)
|
||||
mod = executionLogBatchTypeModel(mod, dao.BatchMain.Columns().BatchType, dao.BatchMain.Columns().BatchInfo, in.BatchType)
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
mod = mod.WhereBetween(dao.BatchMain.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
mod = mod.Page(in.Page, in.PageSize).
|
||||
OrderDesc(dao.BatchMain.Columns().Id).
|
||||
Hook(hook.MemberSummary)
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取执行日志列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
fields := []*allocationunits.Fields{}
|
||||
for _, item := range list {
|
||||
if item.BatchType == consts.BatchTypeAllocate {
|
||||
item.Fields = allocationunits.Resolve(item.UnitInfo, item.CreatedDept, item.StoredTargetDeptId)
|
||||
fields = append(fields, &item.Fields)
|
||||
}
|
||||
}
|
||||
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), fields); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) Summary(ctx context.Context, in *model.BatchExecutionLogDetailParam) (res *model.BatchExecutionLogDetailModel, err error) {
|
||||
if in == nil || in.BatchNo == "" {
|
||||
return nil, gerror.New("批次号不能为空")
|
||||
}
|
||||
|
||||
batch := &batchExecutionLogSummaryBatch{}
|
||||
mod := executionLogBatchTypeModel(
|
||||
s.batchModel(ctx).Where(dao.BatchMain.Columns().BatchNo, in.BatchNo),
|
||||
dao.BatchMain.Columns().BatchType,
|
||||
dao.BatchMain.Columns().BatchInfo,
|
||||
nil,
|
||||
)
|
||||
if err = mod.Scan(batch); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取执行日志详情失败,请稍后重试!")
|
||||
}
|
||||
if batch.Id == 0 {
|
||||
return nil, gerror.New("执行日志不存在")
|
||||
}
|
||||
if isAllocateChildBatch(batch.BatchType, batch.BatchNo, batch.SourceBatchNo) {
|
||||
return nil, gerror.New("执行日志不存在")
|
||||
}
|
||||
|
||||
res = &model.BatchExecutionLogDetailModel{
|
||||
SourceChannel: batch.SourceChannel,
|
||||
ExecutionMode: batch.ExecutionMode,
|
||||
Id: batch.Id,
|
||||
BatchNo: batch.BatchNo,
|
||||
BatchType: batch.BatchType,
|
||||
SourceType: batch.SourceType,
|
||||
BatchAsyncStatus: batch.BatchAsyncStatus,
|
||||
RunResult: batch.RunResult,
|
||||
FailReason: batch.FailReason,
|
||||
TotalCount: batch.TotalCount,
|
||||
StartTime: batch.StartTime,
|
||||
EndTime: batch.EndTime,
|
||||
}
|
||||
|
||||
if batch.BatchType == consts.BatchTypeAllocate {
|
||||
res.Fields = allocationunits.Resolve(batch.BatchInfo, batch.CreatedDept, batch.TargetDeptId)
|
||||
if err := allocationunits.Fill(ctx, contexts.GetTenantId(ctx), []*allocationunits.Fields{&res.Fields}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if sourceFile, err := s.getSourceFile(ctx, batch.SourceOssId, batch.BatchInfo); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
res.SourceFile = sourceFile
|
||||
}
|
||||
|
||||
if resultFile, err := s.getResultFile(ctx, batch.BatchNo, batch.ResultOssId, batch.BatchInfo); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
res.ResultFile = resultFile
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) DetailList(ctx context.Context, in *model.BatchDetailListParam) (list []*model.BatchDetailListModel, totalCount int, err error) {
|
||||
if in == nil || in.BatchNo == "" {
|
||||
return nil, 0, gerror.New("批次号不能为空")
|
||||
}
|
||||
|
||||
batch, err := s.getDetailBatch(ctx, in.BatchNo, in.BatchType)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
columns := dao.BatchDetail.Columns()
|
||||
detailBatchType := batch.BatchType
|
||||
mod := s.detailModel(ctx).
|
||||
Fields(
|
||||
columns.Id,
|
||||
columns.BatchNo,
|
||||
columns.BatchType,
|
||||
columns.DetailStatus,
|
||||
columns.RowNo,
|
||||
columns.ItemId,
|
||||
columns.ErrorReason,
|
||||
columns.Snapshot,
|
||||
columns.TenantId,
|
||||
columns.CreatedDept,
|
||||
columns.CreatedBy,
|
||||
).
|
||||
Where(columns.BatchType, detailBatchType)
|
||||
if isAllocateParentBatch(batch) {
|
||||
childBatches := s.batchModel(ctx).
|
||||
Fields(dao.BatchMain.Columns().BatchNo).
|
||||
Where(dao.BatchMain.Columns().BatchType, consts.BatchTypeAllocate).
|
||||
Where(dao.BatchMain.Columns().SourceBatchNo, batch.BatchNo).
|
||||
Where(dao.BatchMain.Columns().SourceBatchNo + " <> " + dao.BatchMain.Columns().BatchNo)
|
||||
mod = mod.Where("("+columns.BatchNo+" = ? OR "+columns.BatchNo+" IN (?))", batch.BatchNo, childBatches)
|
||||
} else {
|
||||
mod = mod.Where(columns.BatchNo, batch.BatchNo)
|
||||
}
|
||||
|
||||
if in.DetailStatus != nil {
|
||||
mod = mod.Where(columns.DetailStatus, *in.DetailStatus)
|
||||
}
|
||||
if in.Iccid != "" {
|
||||
mod = mod.Where(columns.ItemId+" LIKE ?", "%"+in.Iccid+"%")
|
||||
}
|
||||
if keyword := strings.TrimSpace(in.Keyword); keyword != "" {
|
||||
like := "%" + keyword + "%"
|
||||
mod = mod.Where("("+columns.ItemId+" LIKE ? OR "+columns.ErrorReason+" LIKE ?)", like, like)
|
||||
}
|
||||
|
||||
var details []*entity.BatchDetail
|
||||
mod = mod.Page(in.Page, in.PageSize).OrderAsc(columns.RowNo)
|
||||
if err = mod.ScanAndCount(&details, &totalCount, false); err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "获取批次执行明细列表失败,请稍后重试")
|
||||
}
|
||||
|
||||
list = make([]*model.BatchDetailListModel, 0, len(details))
|
||||
displayRowStart := 0
|
||||
if in.Page > 0 && in.PageSize > 0 {
|
||||
displayRowStart = (in.Page - 1) * in.PageSize
|
||||
}
|
||||
for index, detail := range details {
|
||||
iccid, msisdn, imsi := batchExecutionLogDetailSnapshotCards(detail)
|
||||
item := &model.BatchDetailListModel{
|
||||
RowNo: displayRowStart + index + 1,
|
||||
Iccid: iccid,
|
||||
Msisdn: msisdn,
|
||||
Imsi: imsi,
|
||||
Imei: batchExecutionLogSnapshotValue(detail, "IMEI", "imei", "rawFields.IMEI"),
|
||||
ChanKey: batchExecutionLogSnapshotValue(detail, "chanKey", "chan_key", "rawFields.chanKey", "rawFields.chan_key", "rawFields.通道编号", "rawFields.通道编码", "rawFields.通道编号*"),
|
||||
CardType: batchExecutionLogSnapshotValue(detail, "cardType", "rawFields.cardType", "rawFields.卡类型"),
|
||||
UpPkgCode: batchExecutionLogSnapshotValue(detail, "upPkgCode", "rawFields.upPkgCode", "rawFields.上游套餐编码", "rawFields.上游套餐"),
|
||||
SuppliersCode: batchExecutionLogSnapshotValue(detail, "suppliersCode", "rawFields.suppliersCode", "rawFields.供应商编码", "rawFields.供应商"),
|
||||
UpstreamActivationTime: batchExecutionLogSnapshotValue(detail, "upstreamActivationTime", "rawFields.upstreamActivationTime", "rawFields.上游激活时间", "rawFields.上游给出激活时间"),
|
||||
InsertToPlugEnabled: batchExecutionLogSnapshotValue(detail, "insertToPlugEnabled", "rawFields.insertToPlugEnabled", "rawFields.是否贴片转插拔"),
|
||||
CarrierTrafficCutoffEnabled: batchExecutionLogSnapshotValue(detail, "carrierTrafficCutoffEnabled", "rawFields.carrierTrafficCutoffEnabled", "rawFields.是否运营商达量断网"),
|
||||
AgreementExpiryDate: batchExecutionLogSnapshotValue(detail, "agreementExpiryDate", "rawFields.agreementExpiryDate", "rawFields.协议到期时间", "rawFields.协议期到期时间"),
|
||||
HasTestPeriod: batchExecutionLogSnapshotValue(detail, "hasTestPeriod", "rawFields.hasTestPeriod", "rawFields.是否有测试期"),
|
||||
TestPeriodDays: batchExecutionLogSnapshotValue(detail, "testPeriodDays", "rawFields.testPeriodDays", "rawFields.测试期天数"),
|
||||
TestPeriodLatestExpiryDate: batchExecutionLogSnapshotValue(detail, "testPeriodLatestExpiryDate", "rawFields.testPeriodLatestExpiryDate", "rawFields.测试期最晚过期日期"),
|
||||
HasSilentPeriod: batchExecutionLogSnapshotValue(detail, "hasSilentPeriod", "rawFields.hasSilentPeriod", "rawFields.是否有沉默期"),
|
||||
SilentPeriodDays: batchExecutionLogSnapshotValue(detail, "silentPeriodDays", "rawFields.silentPeriodDays", "rawFields.沉默期天数"),
|
||||
SilentPeriodLatestExpiryDate: batchExecutionLogSnapshotValue(detail, "silentPeriodLatestExpiryDate", "rawFields.silentPeriodLatestExpiryDate", "rawFields.沉默期最晚过期日期"),
|
||||
DetailStatus: detail.DetailStatus,
|
||||
}
|
||||
if batch.BatchType == consts.BatchTypeAllocate {
|
||||
parentUnits := allocationunits.Resolve(batch.BatchInfo, batch.CreatedDept, batch.TargetDeptId)
|
||||
item.Fields = allocationunits.Resolve(detail.Snapshot, parentUnits.SourceDeptId, parentUnits.TargetDeptId)
|
||||
if item.SourceDeptName == "" && item.SourceDeptId == parentUnits.SourceDeptId {
|
||||
item.SourceDeptName = parentUnits.SourceDeptName
|
||||
}
|
||||
if item.TargetDeptName == "" && item.TargetDeptId == parentUnits.TargetDeptId {
|
||||
item.TargetDeptName = parentUnits.TargetDeptName
|
||||
}
|
||||
}
|
||||
|
||||
if detail.ErrorReason != "" {
|
||||
item.Error = &model.BatchDetailErrorModel{
|
||||
Reason: detail.ErrorReason,
|
||||
}
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
fields := []*allocationunits.Fields{}
|
||||
for _, item := range list {
|
||||
if batch.BatchType == consts.BatchTypeAllocate {
|
||||
fields = append(fields, &item.Fields)
|
||||
}
|
||||
}
|
||||
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), fields); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) getDetailBatch(ctx context.Context, batchNo string, batchType *int) (*entity.BatchMain, error) {
|
||||
if batchNo == "" {
|
||||
return nil, gerror.New("批次号不能为空")
|
||||
}
|
||||
|
||||
columns := dao.BatchMain.Columns()
|
||||
batch := &entity.BatchMain{}
|
||||
mod := s.batchModel(ctx).
|
||||
Fields(
|
||||
columns.Id,
|
||||
columns.BatchNo,
|
||||
columns.BatchType,
|
||||
columns.SourceType,
|
||||
columns.SourceBatchNo,
|
||||
columns.BatchInfo,
|
||||
columns.CreatedDept, columns.TargetDeptId,
|
||||
).
|
||||
Where(columns.BatchNo, batchNo)
|
||||
mod = executionLogBatchTypeModel(mod, columns.BatchType, columns.BatchInfo, batchType)
|
||||
if err := mod.Scan(batch); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取批次信息失败,请稍后重试")
|
||||
}
|
||||
if batch.Id == 0 {
|
||||
return nil, gerror.New("批次不存在")
|
||||
}
|
||||
if isAllocateChildBatch(batch.BatchType, batch.BatchNo, batch.SourceBatchNo) {
|
||||
return nil, gerror.New("批次不存在")
|
||||
}
|
||||
if !executionLogBatchTypeMatches(batch.BatchType, batch.BatchInfo, batchType) {
|
||||
return nil, gerror.New("批次类型不匹配")
|
||||
}
|
||||
return batch, nil
|
||||
}
|
||||
|
||||
func executionLogBatchTypeModel(mod *gdb.Model, batchTypeColumn, batchInfoColumn string, requested *int) *gdb.Model {
|
||||
if requested == nil {
|
||||
return mod
|
||||
}
|
||||
if *requested == consts.BatchTypePrepare {
|
||||
return mod.Where(batchTypeColumn, consts.BatchTypePrepare)
|
||||
}
|
||||
if *requested == consts.BatchTypeAllocate {
|
||||
return mod.Where(
|
||||
fmt.Sprintf("(%s = ? OR (%s = ? AND JSON_UNQUOTE(JSON_EXTRACT(%s, '$.targetBatchType')) = ?))", batchTypeColumn, batchTypeColumn, batchInfoColumn),
|
||||
consts.BatchTypeAllocate,
|
||||
consts.BatchTypePrepare,
|
||||
strconv.Itoa(*requested),
|
||||
)
|
||||
}
|
||||
return mod.Where(
|
||||
fmt.Sprintf("(%s = ? OR (%s = ? AND JSON_UNQUOTE(JSON_EXTRACT(%s, '$.targetBatchType')) = ?))", batchTypeColumn, batchTypeColumn, batchInfoColumn),
|
||||
*requested,
|
||||
consts.BatchTypePrepare,
|
||||
strconv.Itoa(*requested),
|
||||
)
|
||||
}
|
||||
|
||||
func executionLogBatchTypeMatches(actual int, info *gjson.Json, requested *int) bool {
|
||||
if requested == nil {
|
||||
return true
|
||||
}
|
||||
if actual == *requested {
|
||||
return true
|
||||
}
|
||||
return actual == consts.BatchTypePrepare && info != nil && info.Get("targetBatchType").Int() == *requested
|
||||
}
|
||||
|
||||
func isAllocateParentBatch(batch *entity.BatchMain) bool {
|
||||
if batch == nil || batch.BatchType != consts.BatchTypeAllocate || isAllocateChildBatch(batch.BatchType, batch.BatchNo, batch.SourceBatchNo) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isAllocateChildBatch(batchType int, batchNo, sourceBatchNo string) bool {
|
||||
return batchType == consts.BatchTypeAllocate &&
|
||||
strings.TrimSpace(sourceBatchNo) != "" &&
|
||||
strings.TrimSpace(sourceBatchNo) != strings.TrimSpace(batchNo)
|
||||
}
|
||||
|
||||
func batchExecutionLogDetailSnapshotCards(detail *entity.BatchDetail) (iccid, msisdn, imsi string) {
|
||||
if detail == nil {
|
||||
return
|
||||
}
|
||||
if detail.Snapshot != nil {
|
||||
iccid = strings.TrimSpace(detail.Snapshot.Get("ICCID").String())
|
||||
msisdn = strings.TrimSpace(detail.Snapshot.Get("MSISDN").String())
|
||||
imsi = strings.TrimSpace(detail.Snapshot.Get("IMSI").String())
|
||||
if iccid == "" {
|
||||
iccid = strings.TrimSpace(detail.Snapshot.Get("iccid").String())
|
||||
}
|
||||
if msisdn == "" {
|
||||
msisdn = strings.TrimSpace(detail.Snapshot.Get("msisdn").String())
|
||||
}
|
||||
if imsi == "" {
|
||||
imsi = strings.TrimSpace(detail.Snapshot.Get("imsi").String())
|
||||
}
|
||||
}
|
||||
if iccid == "" {
|
||||
iccid = detail.ItemId
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func batchExecutionLogSnapshotValue(detail *entity.BatchDetail, paths ...string) string {
|
||||
if detail == nil || detail.Snapshot == nil {
|
||||
return ""
|
||||
}
|
||||
for _, path := range paths {
|
||||
value := strings.TrimSpace(detail.Snapshot.Get(path).String())
|
||||
if value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) getSourceFile(ctx context.Context, sourceOssId int64, batchInfo *gjson.Json) (*model.BatchExecutionLogFileModel, error) {
|
||||
ossId := sourceOssId
|
||||
if ossId == 0 {
|
||||
ossId = legacySourceOssId(batchInfo)
|
||||
}
|
||||
if ossId == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
oss := &entity.SysOss{}
|
||||
if err := s.ossModel(ctx).
|
||||
Where(dao.SysOss.Columns().OssId, ossId).
|
||||
Scan(oss); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取原始文件信息失败,请稍后重试!")
|
||||
}
|
||||
if oss.OssId == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &model.BatchExecutionLogFileModel{
|
||||
OssId: oss.OssId,
|
||||
Url: batchExecutionLogFileURL(ctx, oss.Url, oss.Path),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) getResultFile(ctx context.Context, batchNo string, resultOssId int64, batchInfo *gjson.Json) (*model.BatchExecutionLogResultFileModel, error) {
|
||||
download := &entity.TResourceBatchDownload{}
|
||||
if err := s.downloadModel(ctx).
|
||||
Where(dao.TResourceBatchDownload.Columns().BatchNo, batchNo).
|
||||
OrderDesc(dao.TResourceBatchDownload.Columns().Id).
|
||||
Limit(1).
|
||||
Scan(download); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取结果文件信息失败,请稍后重试!")
|
||||
}
|
||||
ossId := resultOssId
|
||||
if ossId == 0 && download.OssId > 0 {
|
||||
ossId = download.OssId
|
||||
}
|
||||
if ossId == 0 {
|
||||
ossId = legacyResultOssId(batchInfo)
|
||||
}
|
||||
if download.Id == 0 && ossId == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
url := batchExecutionLogFileURL(ctx, download.FileUrl, download.FilePath)
|
||||
if ossId > 0 {
|
||||
oss := &entity.SysOss{}
|
||||
if err := s.ossModel(ctx).
|
||||
Where(dao.SysOss.Columns().OssId, ossId).
|
||||
Scan(oss); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取结果文件OSS信息失败,请稍后重试!")
|
||||
}
|
||||
if oss.OssId > 0 {
|
||||
if ossUrl := batchExecutionLogFileURL(ctx, oss.Url, oss.Path); ossUrl != "" {
|
||||
url = ossUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &model.BatchExecutionLogResultFileModel{
|
||||
DownloadId: download.Id,
|
||||
OssId: ossId,
|
||||
Url: url,
|
||||
DownloadStatus: resultDownloadStatus(download),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func legacySourceOssId(batchInfo *gjson.Json) int64 {
|
||||
if batchInfo == nil {
|
||||
return 0
|
||||
}
|
||||
for _, path := range []string{"importFileOssId", "source.importFileOssId", "source.inputFileOssId"} {
|
||||
if ossId := batchInfo.Get(path).Int64(); ossId > 0 {
|
||||
return ossId
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func legacyResultOssId(batchInfo *gjson.Json) int64 {
|
||||
if batchInfo == nil {
|
||||
return 0
|
||||
}
|
||||
return batchInfo.Get("resultFileOssId").Int64()
|
||||
}
|
||||
|
||||
func resultDownloadStatus(download *entity.TResourceBatchDownload) int {
|
||||
if download != nil && download.Id > 0 {
|
||||
return download.DownloadStatus
|
||||
}
|
||||
return consts.DownloadStatusGenerated
|
||||
}
|
||||
|
||||
func batchExecutionLogFileURL(ctx context.Context, storedURL string, storedPath string) string {
|
||||
if url := normalizeBatchLocalUploadURL(ctx, storedURL); url != "" {
|
||||
return url
|
||||
}
|
||||
if url := normalizeBatchLocalUploadURL(ctx, storedPath); url != "" {
|
||||
return url
|
||||
}
|
||||
return strings.TrimSpace(storedURL)
|
||||
}
|
||||
|
||||
func normalizeBatchLocalUploadURL(ctx context.Context, value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
lower := strings.ToLower(value)
|
||||
if strings.HasPrefix(lower, "http://") || strings.HasPrefix(lower, "https://") {
|
||||
return value
|
||||
}
|
||||
normalized := filepath.ToSlash(filepath.Clean(value))
|
||||
if normalized == "/upload" || strings.HasPrefix(normalized, "/upload/") {
|
||||
return normalized
|
||||
}
|
||||
|
||||
if configType, err := service.SysConfig().GetConfigByKey(ctx, consts.ConfigOssUrlPath); err == nil && configType != nil {
|
||||
configValue := strings.TrimSpace(configType.ConfigValue)
|
||||
if configValue != "" {
|
||||
if strings.Contains(configValue, "|") {
|
||||
split := strings.SplitN(configValue, "|", 2)
|
||||
from := strings.TrimSpace(filepath.ToSlash(split[0]))
|
||||
to := strings.TrimSpace(filepath.ToSlash(split[1]))
|
||||
if suffix, ok := batchLocalUploadMappedSuffix(normalized, from); ok {
|
||||
return batchJoinUploadURL(to, suffix)
|
||||
}
|
||||
} else if suffix := batchLocalUploadRelativePath(normalized); suffix != "" {
|
||||
return batchJoinUploadURL(configValue, suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if suffix := batchLocalUploadRelativePath(normalized); suffix != "" {
|
||||
return batchJoinUploadURL("/upload", suffix)
|
||||
}
|
||||
if strings.HasPrefix(normalized, "/") {
|
||||
return normalized
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func batchLocalUploadMappedSuffix(filePath string, from string) (string, bool) {
|
||||
from = strings.Trim(strings.TrimSpace(filepath.ToSlash(from)), "/")
|
||||
if from == "" {
|
||||
return "", false
|
||||
}
|
||||
normalized := strings.TrimSpace(filepath.ToSlash(filePath))
|
||||
marker := "/" + from + "/"
|
||||
if idx := strings.Index(normalized, marker); idx >= 0 {
|
||||
return strings.TrimPrefix(normalized[idx+len(marker):], "/"), true
|
||||
}
|
||||
prefix := from + "/"
|
||||
if strings.HasPrefix(normalized, prefix) {
|
||||
return strings.TrimPrefix(normalized[len(prefix):], "/"), true
|
||||
}
|
||||
if strings.Trim(normalized, "/") == from {
|
||||
return "", true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func batchLocalUploadRelativePath(filePath string) string {
|
||||
normalized := filepath.ToSlash(filepath.Clean(strings.TrimSpace(filePath)))
|
||||
for _, marker := range []string{
|
||||
"resource/upload/",
|
||||
"/resource/upload/",
|
||||
"/app/resource/upload/",
|
||||
"/app/upload/",
|
||||
"upload/",
|
||||
"/upload/",
|
||||
} {
|
||||
if idx := strings.Index(normalized, marker); idx >= 0 {
|
||||
return strings.TrimPrefix(normalized[idx+len(marker):], "/")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func batchJoinUploadURL(base string, suffix string) string {
|
||||
base = strings.TrimRight(strings.TrimSpace(filepath.ToSlash(base)), "/")
|
||||
suffix = strings.TrimLeft(strings.TrimSpace(filepath.ToSlash(suffix)), "/")
|
||||
if base == "" {
|
||||
base = "/upload"
|
||||
}
|
||||
if suffix == "" {
|
||||
return base
|
||||
}
|
||||
return base + "/" + suffix
|
||||
}
|
||||
@@ -0,0 +1,667 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||
// @AutoGenerate Version
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/allocationunits"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchmetadata"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rabbitmq"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
||||
"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/do"
|
||||
"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"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonBatchMain struct{}
|
||||
|
||||
const restartPhaseWaitingResume = "waiting_resume"
|
||||
|
||||
type batchExecuteMessage struct {
|
||||
BatchNo string `json:"batchNo"`
|
||||
BatchType int `json:"batchType"`
|
||||
}
|
||||
|
||||
var publishBatchExecuteMessage = rabbitmq.Publish
|
||||
|
||||
func NewCommonBatchMain() *sCommonBatchMain {
|
||||
return &sCommonBatchMain{}
|
||||
}
|
||||
|
||||
func (s *sCommonBatchMain) getRequeueBatch(ctx context.Context, batchNo string) (*entity.BatchMain, error) {
|
||||
columns := dao.BatchMain.Columns()
|
||||
batch := &entity.BatchMain{}
|
||||
err := s.Model(ctx).
|
||||
Fields(
|
||||
columns.Id,
|
||||
columns.BatchNo,
|
||||
columns.BatchType,
|
||||
columns.BatchAsyncStatus,
|
||||
columns.RunResult,
|
||||
columns.BatchInfo,
|
||||
columns.TenantId,
|
||||
columns.DeptId,
|
||||
columns.CreatedDept,
|
||||
columns.CreatedBy,
|
||||
).
|
||||
Where(columns.BatchNo, batchNo).
|
||||
Where(columns.BatchType+" <> ?", consts.BatchTypePrepare).
|
||||
Scan(batch)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "查询批次信息失败")
|
||||
}
|
||||
if batch.Id == 0 {
|
||||
return nil, gerror.New("批次不存在或无权限")
|
||||
}
|
||||
return batch, nil
|
||||
}
|
||||
|
||||
func validateRequeueBatch(batch *entity.BatchMain) error {
|
||||
if batch == nil {
|
||||
return gerror.New("批次不存在")
|
||||
}
|
||||
switch batch.BatchAsyncStatus {
|
||||
case consts.BatchAsyncStatusPending:
|
||||
return nil
|
||||
case consts.BatchAsyncStatusProcessing:
|
||||
return validateProcessingRequeueBatch(batch)
|
||||
case consts.BatchAsyncStatusFailed, consts.BatchAsyncStatusSuccess, consts.BatchAsyncStatusPartialSuccess:
|
||||
return gerror.New("终态批次不支持安全重投")
|
||||
default:
|
||||
return gerror.Newf("当前批次状态不支持安全重投,status=%d", batch.BatchAsyncStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func validateProcessingRequeueBatch(batch *entity.BatchMain) error {
|
||||
if batch.BatchType != consts.BatchTypeRestart {
|
||||
return gerror.New("处理中批次不支持安全重投,避免重复调用运营商")
|
||||
}
|
||||
if restartPhase(batch) != restartPhaseWaitingResume {
|
||||
return gerror.New("RST 批次当前不在等待复机阶段,不能安全重投")
|
||||
}
|
||||
resumeAt := restartResumeAt(batch)
|
||||
if resumeAt == nil {
|
||||
return gerror.New("RST 批次缺少计划复机时间,不能安全重投")
|
||||
}
|
||||
if resumeAt.After(gtime.Now()) {
|
||||
return gerror.Newf("未到计划复机时间,resumeAt=%s", resumeAt.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func restartPhase(batch *entity.BatchMain) string {
|
||||
if batch == nil || batch.BatchInfo == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(batch.BatchInfo.Get("restart.phase").String())
|
||||
}
|
||||
|
||||
func restartResumeAt(batch *entity.BatchMain) *gtime.Time {
|
||||
if batch == nil || batch.BatchInfo == nil {
|
||||
return nil
|
||||
}
|
||||
value := strings.TrimSpace(batch.BatchInfo.Get("restart.resumeAt").String())
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
parsed := gtime.NewFromStr(value)
|
||||
if parsed == nil || parsed.IsZero() {
|
||||
return nil
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonBatchMain(NewCommonBatchMain())
|
||||
}
|
||||
|
||||
// Model 批次主表ORM模型
|
||||
func (s *sCommonBatchMain) 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.BatchMain.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
func (s *sCommonBatchMain) detailModel(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.BatchDetail.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取批次主表列表
|
||||
func (s *sCommonBatchMain) List(ctx context.Context, in *model.BatchMainListParam) (list []*model.BatchMainListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.BatchMainListModel{})
|
||||
|
||||
// 查询主键ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.BatchMain.Columns().Id, in.Id)
|
||||
}
|
||||
// 查询批次编号
|
||||
if in.BatchNo != "" {
|
||||
mod = mod.Where(dao.BatchMain.Columns().BatchNo+" LIKE ?", "%"+in.BatchNo+"%")
|
||||
}
|
||||
// 查询订单编号
|
||||
if in.OrderSn != "" {
|
||||
mod = mod.Where(dao.BatchMain.Columns().OrderSn+" LIKE ?", "%"+in.OrderSn+"%")
|
||||
}
|
||||
|
||||
// 查询批次类型 枚举
|
||||
mod = visibleBatchTypeModel(mod, dao.BatchMain.Columns().BatchType, in.BatchType)
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.BatchMain.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.BatchMain.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取批次主表列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
units := make([]*allocationunits.Fields, 0, len(list))
|
||||
for _, item := range list {
|
||||
if item.BatchType == consts.BatchTypeAllocate {
|
||||
fields := allocationunits.Resolve(item.BatchInfo, item.CreatedDept, item.TargetDeptId)
|
||||
units = append(units, &fields)
|
||||
}
|
||||
}
|
||||
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), units); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
unitIndex := 0
|
||||
for _, item := range list {
|
||||
if item.BatchType == consts.BatchTypeAllocate {
|
||||
fields := units[unitIndex]
|
||||
item.SourceDeptId, item.SourceDeptName = fields.SourceDeptId, fields.SourceDeptName
|
||||
item.TargetDeptId, item.TargetDeptName, item.TargetDeptMode = fields.TargetDeptId, fields.TargetDeptName, fields.TargetDeptMode
|
||||
unitIndex++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonBatchMain) SuccessList(ctx context.Context, in *model.BatchMainSuccessListParam) (list []*model.BatchMainSuccessListModel, totalCount int, err error) {
|
||||
if in == nil {
|
||||
in = &model.BatchMainSuccessListParam{}
|
||||
}
|
||||
|
||||
columns := dao.BatchMain.Columns()
|
||||
mod := s.Model(ctx).
|
||||
Fields(
|
||||
columns.BatchInfo,
|
||||
columns.BatchNo,
|
||||
columns.BatchType,
|
||||
columns.StartTime+" AS batch_time",
|
||||
columns.TotalCount,
|
||||
columns.CreatedDept,
|
||||
columns.TargetDeptId,
|
||||
).
|
||||
Where(columns.RunResult, consts.BatchRunResultSuccess).
|
||||
WhereGT(columns.TotalCount, 0)
|
||||
|
||||
var requestedBatchType *int
|
||||
if in.BatchType != 0 {
|
||||
requestedBatchType = &in.BatchType
|
||||
}
|
||||
mod = visibleBatchTypeModel(mod, columns.BatchType, requestedBatchType)
|
||||
|
||||
if in.BatchNo != "" {
|
||||
mod = mod.Where(columns.BatchNo+" LIKE ?", "%"+in.BatchNo+"%")
|
||||
}
|
||||
if len(in.Time) == 2 {
|
||||
in.Time[1] = in.Time[1].EndOfDay()
|
||||
mod = mod.WhereBetween(columns.StartTime, in.Time[0], in.Time[1])
|
||||
}
|
||||
|
||||
mod = mod.Page(in.Page, in.PageSize).OrderDesc(columns.Id)
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "获取成功批次列表失败,请稍后重试")
|
||||
}
|
||||
if err = fillInboundBatchStockCounts(ctx, list); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
units := make([]*allocationunits.Fields, 0, len(list))
|
||||
for _, item := range list {
|
||||
if item.BatchType == consts.BatchTypeAllocate {
|
||||
item.Fields = allocationunits.Resolve(item.BatchInfo, item.StoredSourceDeptId, item.StoredTargetDeptId)
|
||||
units = append(units, &item.Fields)
|
||||
}
|
||||
}
|
||||
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), units); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fillInboundBatchStockCounts(ctx context.Context, list []*model.BatchMainSuccessListModel) error {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
batchNos := make([]string, 0, len(list))
|
||||
for _, item := range list {
|
||||
if item == nil || item.BatchType != consts.BatchTypeInbound || strings.TrimSpace(item.BatchNo) == "" {
|
||||
continue
|
||||
}
|
||||
batchNos = append(batchNos, item.BatchNo)
|
||||
}
|
||||
if len(batchNos) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
type stockCountRow struct {
|
||||
BatchNo string `orm:"batch_no"`
|
||||
StockCount int `orm:"stock_count"`
|
||||
}
|
||||
var rows []*stockCountRow
|
||||
stockColumns := dao.TSimCardStock.Columns()
|
||||
if err := dao.TSimCardStock.Ctx(ctx).
|
||||
Fields(stockColumns.BatchNo, "COUNT(1) AS stock_count").
|
||||
Where(stockColumns.TenantId, contexts.GetTenantId(ctx)).
|
||||
WhereIn(stockColumns.BatchNo, batchNos).
|
||||
Group(stockColumns.BatchNo).
|
||||
Scan(&rows); err != nil {
|
||||
return gerror.Wrap(err, "统计入库批次在库数量失败")
|
||||
}
|
||||
|
||||
stockCountMap := make(map[string]int, len(rows))
|
||||
for _, row := range rows {
|
||||
if row != nil {
|
||||
stockCountMap[row.BatchNo] = row.StockCount
|
||||
}
|
||||
}
|
||||
for _, item := range list {
|
||||
if item != nil && item.BatchType == consts.BatchTypeInbound {
|
||||
item.StockCount = stockCountMap[item.BatchNo]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type batchDetailQuery struct {
|
||||
BatchNo string
|
||||
BatchType *int
|
||||
DetailStatus *int
|
||||
Iccid string
|
||||
Msisdn string
|
||||
CurrentStockOnly bool
|
||||
Page int
|
||||
PageSize int
|
||||
}
|
||||
|
||||
func (s *sCommonBatchMain) ListCards(ctx context.Context, in *model.BatchCardListParam) (list []*model.BatchCardListModel, totalCount int, err error) {
|
||||
if in == nil || in.BatchNo == "" {
|
||||
return nil, 0, gerror.New("批次号不能为空")
|
||||
}
|
||||
batch, err := s.getVisibleBatch(ctx, in.BatchNo, in.BatchType)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
details, totalCount, err := s.listBatchDetails(ctx, batch, batchDetailQuery{
|
||||
BatchNo: in.BatchNo,
|
||||
BatchType: in.BatchType,
|
||||
Iccid: in.Iccid,
|
||||
Msisdn: in.Msisdn,
|
||||
CurrentStockOnly: in.CurrentStockOnly,
|
||||
Page: in.Page,
|
||||
PageSize: in.PageSize,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list = make([]*model.BatchCardListModel, 0, len(details))
|
||||
displayRowStart := 0
|
||||
if in.Page > 0 && in.PageSize > 0 {
|
||||
displayRowStart = (in.Page - 1) * in.PageSize
|
||||
}
|
||||
units := make([]*allocationunits.Fields, 0, len(details))
|
||||
parentUnits := allocationunits.Resolve(batch.BatchInfo, batch.CreatedDept, batch.TargetDeptId)
|
||||
for index, detail := range details {
|
||||
iccid, msisdn, imsi := batchDetailSnapshotCards(detail)
|
||||
item := &model.BatchCardListModel{
|
||||
RowNo: displayRowStart + index + 1,
|
||||
Iccid: iccid,
|
||||
Msisdn: msisdn,
|
||||
Imsi: imsi,
|
||||
DetailStatus: detail.DetailStatus,
|
||||
}
|
||||
if batch.BatchType == consts.BatchTypeAllocate {
|
||||
item.Fields = allocationunits.Resolve(detail.Snapshot, parentUnits.SourceDeptId, parentUnits.TargetDeptId)
|
||||
if item.SourceDeptName == "" && item.SourceDeptId == parentUnits.SourceDeptId {
|
||||
item.SourceDeptName = parentUnits.SourceDeptName
|
||||
}
|
||||
if item.TargetDeptName == "" && item.TargetDeptId == parentUnits.TargetDeptId {
|
||||
item.TargetDeptName = parentUnits.TargetDeptName
|
||||
}
|
||||
units = append(units, &item.Fields)
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), units); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// listBatchDetails 获取批次卡清单明细。
|
||||
func (s *sCommonBatchMain) listBatchDetails(ctx context.Context, batch *entity.BatchMain, query batchDetailQuery) (list []*entity.BatchDetail, totalCount int, err error) {
|
||||
columns := dao.BatchDetail.Columns()
|
||||
mod := s.detailModel(ctx).
|
||||
Fields(
|
||||
columns.Id,
|
||||
columns.BatchNo,
|
||||
columns.BatchType,
|
||||
columns.DetailStatus,
|
||||
columns.RowNo,
|
||||
columns.ItemId,
|
||||
columns.ErrorReason,
|
||||
columns.Snapshot,
|
||||
columns.TenantId,
|
||||
columns.CreatedDept,
|
||||
columns.CreatedBy,
|
||||
).
|
||||
Where(columns.BatchType, batch.BatchType)
|
||||
if batch.BatchType == consts.BatchTypeAllocate && (batch.SourceBatchNo == "" || batch.SourceBatchNo == batch.BatchNo) {
|
||||
mainColumns := dao.BatchMain.Columns()
|
||||
childBatches := s.Model(ctx).Fields(mainColumns.BatchNo).
|
||||
Where(mainColumns.BatchType, consts.BatchTypeAllocate).
|
||||
Where(mainColumns.SourceBatchNo, batch.BatchNo).
|
||||
Where(mainColumns.SourceBatchNo + " <> " + mainColumns.BatchNo)
|
||||
mod = mod.Where("("+columns.BatchNo+" = ? OR "+columns.BatchNo+" IN (?))", batch.BatchNo, childBatches)
|
||||
} else {
|
||||
mod = mod.Where(columns.BatchNo, batch.BatchNo)
|
||||
}
|
||||
|
||||
if query.DetailStatus != nil {
|
||||
mod = mod.Where(columns.DetailStatus, *query.DetailStatus)
|
||||
}
|
||||
if query.Iccid != "" {
|
||||
like := "%" + query.Iccid + "%"
|
||||
mod = mod.Where(
|
||||
"("+columns.ItemId+" LIKE ? OR JSON_UNQUOTE(JSON_EXTRACT("+columns.Snapshot+", '$.ICCID')) LIKE ?)",
|
||||
like,
|
||||
like,
|
||||
)
|
||||
}
|
||||
if query.Msisdn != "" {
|
||||
mod = mod.Where(
|
||||
"JSON_UNQUOTE(JSON_EXTRACT("+columns.Snapshot+", '$.MSISDN')) LIKE ?",
|
||||
"%"+query.Msisdn+"%",
|
||||
)
|
||||
}
|
||||
if query.CurrentStockOnly {
|
||||
mod = onlyCurrentStockDetails(mod)
|
||||
}
|
||||
|
||||
mod = mod.Page(query.Page, query.PageSize).OrderAsc(columns.RowNo)
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "获取批次明细列表失败,请稍后重试")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func onlyCurrentStockDetails(mod *gdb.Model) *gdb.Model {
|
||||
detailTable := dao.BatchDetail.Table()
|
||||
stockTable := dao.TSimCardStock.Table()
|
||||
return mod.Where(fmt.Sprintf(
|
||||
"EXISTS (SELECT 1 FROM `%s` stock WHERE stock.`tenant_id` = `%s`.`tenant_id` AND stock.`batch_no` = `%s`.`batch_no` AND stock.`iccid` = COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(`%s`.`snapshot`, '$.ICCID')), ''), `%s`.`item_id`))",
|
||||
stockTable,
|
||||
detailTable,
|
||||
detailTable,
|
||||
detailTable,
|
||||
detailTable,
|
||||
))
|
||||
}
|
||||
|
||||
func (s *sCommonBatchMain) getVisibleBatch(ctx context.Context, batchNo string, batchType *int) (*entity.BatchMain, error) {
|
||||
if batchNo == "" {
|
||||
return nil, gerror.New("批次号不能为空")
|
||||
}
|
||||
|
||||
columns := dao.BatchMain.Columns()
|
||||
batch := &entity.BatchMain{}
|
||||
mod := s.Model(ctx).
|
||||
Fields(
|
||||
columns.Id,
|
||||
columns.BatchNo,
|
||||
columns.BatchType,
|
||||
columns.TenantId,
|
||||
columns.DeptId,
|
||||
columns.BatchInfo,
|
||||
columns.TargetDeptId,
|
||||
columns.SourceBatchNo,
|
||||
columns.CreatedDept,
|
||||
columns.CreatedBy,
|
||||
).
|
||||
Where(columns.BatchNo, batchNo)
|
||||
mod = visibleBatchTypeModel(mod, columns.BatchType, batchType)
|
||||
if err := mod.Scan(batch); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取批次信息失败,请稍后重试")
|
||||
}
|
||||
if batch.Id == 0 {
|
||||
return nil, gerror.New("批次不存在")
|
||||
}
|
||||
if !visibleBatchTypeMatches(batch.BatchType, batchType) {
|
||||
return nil, gerror.New("批次类型不匹配")
|
||||
}
|
||||
return batch, nil
|
||||
}
|
||||
|
||||
func batchDetailSnapshotCards(detail *entity.BatchDetail) (iccid, msisdn, imsi string) {
|
||||
if detail == nil {
|
||||
return
|
||||
}
|
||||
if detail.Snapshot != nil {
|
||||
iccid = strings.TrimSpace(detail.Snapshot.Get("ICCID").String())
|
||||
msisdn = strings.TrimSpace(detail.Snapshot.Get("MSISDN").String())
|
||||
imsi = strings.TrimSpace(detail.Snapshot.Get("IMSI").String())
|
||||
}
|
||||
if iccid == "" {
|
||||
iccid = detail.ItemId
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出批次主表
|
||||
func (s *sCommonBatchMain) Export(ctx context.Context, in *model.BatchMainListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.BatchMainExportModel{})
|
||||
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.BatchMainExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增批次主表
|
||||
func (s *sCommonBatchMain) Edit(ctx context.Context, in *model.BatchMainEditParam) (err error) {
|
||||
if in == nil {
|
||||
return gerror.New("批次参数不能为空")
|
||||
}
|
||||
if in.Id <= 0 {
|
||||
if in.ExecutionMode == nil {
|
||||
return gerror.New("新建批次必须指定单卡或批量操作规模")
|
||||
}
|
||||
if err = batchmetadata.ValidateCreation(batchmetadata.Console, *in.ExecutionMode); err != nil {
|
||||
return err
|
||||
}
|
||||
if *in.ExecutionMode == batchmetadata.Single && (in.TotalCount == nil || *in.TotalCount != 1) {
|
||||
return gerror.New("单卡批次总数量必须为1")
|
||||
}
|
||||
}
|
||||
data := do.BatchMain{}
|
||||
if err = gconv.Struct(in, &data); err != nil {
|
||||
return gerror.Wrap(err, "转换批次参数失败")
|
||||
}
|
||||
data.CreatedAt = nil
|
||||
data.UpdatedAt = nil
|
||||
// 验证'BatchNo'唯一
|
||||
if err = xgorm.IsUnique(ctx, &dao.BatchMain, g.Map{dao.BatchMain.Columns().BatchNo: in.BatchNo}, "批次编号已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
data.UpdatedBy = contexts.GetUserId(ctx)
|
||||
data.SourceChannel = nil
|
||||
data.ExecutionMode = nil
|
||||
if _, err = s.Model(ctx).TX(tx).
|
||||
Fields(model.BatchMainUpdateFields{}).
|
||||
WherePri(in.Id).Data(data).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改批次主表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
data.Id = nil
|
||||
data.TenantId = contexts.GetTenantId(ctx)
|
||||
data.CreatedDept = contexts.GetDeptId(ctx)
|
||||
data.CreatedBy = contexts.GetUserId(ctx)
|
||||
data.SourceChannel = batchmetadata.Console
|
||||
data.ExecutionMode = *in.ExecutionMode
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).TX(tx).
|
||||
Fields(model.BatchMainInsertFields{}).
|
||||
Data(data).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增批次主表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除批次主表
|
||||
func (s *sCommonBatchMain) Delete(ctx context.Context, in *model.BatchMainDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除批次主表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取批次主表指定信息
|
||||
func (s *sCommonBatchMain) Requeue(ctx context.Context, in *model.BatchMainRequeueParam) (res *model.BatchMainRequeueModel, err error) {
|
||||
if in == nil {
|
||||
in = &model.BatchMainRequeueParam{}
|
||||
}
|
||||
batchNo := strings.TrimSpace(in.BatchNo)
|
||||
reason := strings.TrimSpace(in.Reason)
|
||||
if batchNo == "" {
|
||||
return nil, gerror.New("批次编号不能为空")
|
||||
}
|
||||
if reason == "" {
|
||||
return nil, gerror.New("重投原因不能为空")
|
||||
}
|
||||
|
||||
batch, err := s.getRequeueBatch(ctx, batchNo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = validateRequeueBatch(batch); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
message := batchExecuteMessage{BatchNo: batch.BatchNo, BatchType: batch.BatchType}
|
||||
if err = publishBatchExecuteMessage(ctx, consts.QueueBatchExecute, message); err != nil {
|
||||
g.Log().Errorf(ctx, "batch requeue publish failed, batchNo=%s, batchType=%d, tenantId=%s, userId=%d, reason=%s, err=%+v",
|
||||
batch.BatchNo, batch.BatchType, contexts.GetTenantId(ctx), contexts.GetUserId(ctx), reason, err)
|
||||
return nil, gerror.Wrap(err, "发布批次重投消息失败")
|
||||
}
|
||||
g.Log().Infof(ctx, "batch requeue published, batchNo=%s, batchType=%d, status=%d, runResult=%d, tenantId=%s, userId=%d, reason=%s",
|
||||
batch.BatchNo, batch.BatchType, batch.BatchAsyncStatus, batch.RunResult, contexts.GetTenantId(ctx), contexts.GetUserId(ctx), reason)
|
||||
|
||||
return &model.BatchMainRequeueModel{
|
||||
BatchNo: batch.BatchNo,
|
||||
BatchType: batch.BatchType,
|
||||
BatchAsyncStatus: batch.BatchAsyncStatus,
|
||||
RunResult: batch.RunResult,
|
||||
Requeued: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *sCommonBatchMain) View(ctx context.Context, in *model.BatchMainViewParam) (res *model.BatchMainViewModel, err error) {
|
||||
mod := visibleBatchTypeModel(
|
||||
s.Model(ctx).WherePri(in.Id),
|
||||
dao.BatchMain.Columns().BatchType,
|
||||
nil,
|
||||
)
|
||||
if err = mod.Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取批次主表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if res == nil || res.Id == 0 {
|
||||
return nil, gerror.New("批次不存在")
|
||||
}
|
||||
if res.BatchType == consts.BatchTypeAllocate {
|
||||
fields := allocationunits.Resolve(res.BatchInfo, res.CreatedDept, res.TargetDeptId)
|
||||
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), []*allocationunits.Fields{&fields}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.SourceDeptId, res.SourceDeptName = fields.SourceDeptId, fields.SourceDeptName
|
||||
res.TargetDeptId, res.TargetDeptName, res.TargetDeptMode = fields.TargetDeptId, fields.TargetDeptName, fields.TargetDeptMode
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
)
|
||||
|
||||
// visibleBatchTypeModel always excludes internal prepare batches before applying
|
||||
// an optional caller-provided batch type filter.
|
||||
func visibleBatchTypeModel(mod *gdb.Model, column string, requested *int) *gdb.Model {
|
||||
mod = mod.Where(column+" <> ?", consts.BatchTypePrepare)
|
||||
if requested != nil {
|
||||
mod = mod.Where(column, *requested)
|
||||
}
|
||||
return mod
|
||||
}
|
||||
|
||||
func visibleBatchTypeMatches(actual int, requested *int) bool {
|
||||
if actual == consts.BatchTypePrepare {
|
||||
return false
|
||||
}
|
||||
return requested == nil || actual == *requested
|
||||
}
|
||||
@@ -0,0 +1,837 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/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/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTBalanceDetail struct{}
|
||||
|
||||
func NewCommonTBalanceDetail() *sCommonTBalanceDetail {
|
||||
return &sCommonTBalanceDetail{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTBalanceDetail(NewCommonTBalanceDetail())
|
||||
}
|
||||
|
||||
// Model 余额明细表ORM模型
|
||||
func (s *sCommonTBalanceDetail) 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.TBalanceDetail.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取余额明细表列表
|
||||
func (s *sCommonTBalanceDetail) List(ctx context.Context, in *model.TBalanceDetailListParam) (list []*model.TBalanceDetailListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
sysDeptTable := dao.SysDept.Table()
|
||||
orderTable := dao.TOrder.Table()
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.FieldsPrefix(dao.TBalanceDetail.Table(), model.TBalanceDetailListModel{})
|
||||
mod = mod.Fields(sysDeptTable + ".dept_name as deptName")
|
||||
mod = mod.Fields(orderTable + ".order_type as orderType")
|
||||
mod = mod.Fields(orderTable + ".pay_type as payType")
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Where(dao.TBalanceDetail.Columns().DeptId, deptId)
|
||||
|
||||
// 查询明细ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TBalanceDetail.Columns().Id, in.Id)
|
||||
}
|
||||
g.Log().Info(ctx, "month==:", in.Month, " len:", len(in.Month))
|
||||
// 查询收支类型
|
||||
if in.IncomeExpenseType != nil && *in.IncomeExpenseType >= 0 {
|
||||
mod = mod.Where(dao.TBalanceDetail.Columns().IncomeExpenseType, *in.IncomeExpenseType)
|
||||
}
|
||||
// 查询交易类型
|
||||
if in.TransactionType != nil && *in.TransactionType >= 0 {
|
||||
mod = mod.Where(dao.TBalanceDetail.Columns().TransactionType, *in.TransactionType)
|
||||
}
|
||||
// 查询交易渠道
|
||||
if in.TransactionChannel != nil && *in.TransactionChannel >= 0 {
|
||||
mod = mod.Where(dao.TBalanceDetail.Columns().TransactionChannel, *in.TransactionChannel)
|
||||
}
|
||||
// // 查询支付类型
|
||||
// if in.PayType != nil && *in.PayType >= 0 {
|
||||
// mod = mod.Where(dao.TBalanceDetail.Columns().PayType, *in.PayType)
|
||||
// }
|
||||
|
||||
// 查询交易时间范围
|
||||
if len(in.Month) == 2 {
|
||||
t1, err := time.ParseInLocation("2006-01", in.Month[0], time.Local)
|
||||
t2, err2 := time.ParseInLocation("2006-01", in.Month[1], time.Local)
|
||||
if err == nil && err2 == nil {
|
||||
startOfMonth := gtime.NewFromTime(t1).StartOfMonth()
|
||||
endOfMonth := gtime.NewFromTime(t2).EndOfMonth()
|
||||
mod = mod.WhereGTE(dao.TBalanceDetail.Columns().TransactionTime, startOfMonth)
|
||||
mod = mod.WhereLTE(dao.TBalanceDetail.Columns().TransactionTime, endOfMonth)
|
||||
}
|
||||
}
|
||||
// 查询账期(根据 transaction_time 筛选月份)
|
||||
if in.AccountPeriod != "" {
|
||||
// 统一转换为 "2026-06" 格式后解析
|
||||
accountPeriodStr := in.AccountPeriod
|
||||
if len(accountPeriodStr) == 6 {
|
||||
accountPeriodStr = accountPeriodStr[:4] + "-" + accountPeriodStr[4:]
|
||||
}
|
||||
t := gtime.NewFromStr(accountPeriodStr + "-01")
|
||||
startOfMonth := t.StartOfDay()
|
||||
endOfMonth := startOfMonth.EndOfMonth()
|
||||
mod = mod.WhereBetween(dao.TBalanceDetail.Columns().TransactionTime, startOfMonth, endOfMonth)
|
||||
}
|
||||
|
||||
mod = mod.LeftJoin(sysDeptTable, dao.TBalanceDetail.Table()+"."+dao.TBalanceDetail.Columns().DeptId+" COLLATE utf8mb4_general_ci="+sysDeptTable+"."+dao.SysDept.Columns().DeptId)
|
||||
mod = mod.LeftJoin(orderTable, dao.TBalanceDetail.Table()+"."+dao.TBalanceDetail.Columns().OrderSn+" COLLATE utf8mb4_general_ci="+orderTable+"."+dao.TOrder.Columns().OrderSn)
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TBalanceDetail.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取余额明细表列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
// 根据 TransactionTime 赋值账期
|
||||
for i := range list {
|
||||
if list[i].TransactionTime != nil {
|
||||
list[i].TradingMonth = list[i].TransactionTime.Format("Y-m")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出余额明细表
|
||||
func (s *sCommonTBalanceDetail) Export(ctx context.Context, in *model.TBalanceDetailListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TBalanceDetailExportModel{})
|
||||
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.TBalanceDetailExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增余额明细表
|
||||
func (s *sCommonTBalanceDetail) Edit(ctx context.Context, in *model.TBalanceDetailEditParam) (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.TBalanceDetailUpdateFields{}).
|
||||
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.TBalanceDetailInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增余额明细表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除余额明细表
|
||||
func (s *sCommonTBalanceDetail) Delete(ctx context.Context, in *model.TBalanceDetailDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除余额明细表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取余额明细表指定信息
|
||||
func (s *sCommonTBalanceDetail) View(ctx context.Context, in *model.TBalanceDetailViewParam) (res *model.TBalanceDetailViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取余额明细表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateBalanceTransferDetail 写入余额转可提现相关的余额明细。
|
||||
func (s *sCommonTBalanceDetail) CreateBalanceTransferDetail(ctx context.Context, tx gdb.TX, in *model.TBalanceTransferDetailCreateParam) error {
|
||||
if in == nil {
|
||||
return gerror.New("余额明细参数不能为空")
|
||||
}
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
userId := contexts.GetUserId(ctx)
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
createdAt := gtime.Now()
|
||||
columns := dao.TBalanceDetail.Columns()
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TBalanceDetail.Table()).Data(g.Map{
|
||||
columns.DeptId: in.DeptId,
|
||||
columns.SerialNo: in.SerialNo,
|
||||
columns.TransactionTime: in.TransactionTime,
|
||||
columns.IncomeExpenseType: in.IncomeExpenseType,
|
||||
columns.TransactionType: in.TransactionType,
|
||||
columns.TransactionChannel: in.TransactionChannel,
|
||||
columns.Amount: in.Amount,
|
||||
columns.BeforeBalance: in.BeforeBalance,
|
||||
columns.BalanceAfter: in.BalanceAfter,
|
||||
columns.OrderSn: in.OrderSn,
|
||||
columns.Remark: in.Remark,
|
||||
columns.TenantId: tenantId,
|
||||
columns.CreatedDept: deptId,
|
||||
columns.CreatedBy: userId,
|
||||
columns.CreatedAt: createdAt,
|
||||
}).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增余额转可提现余额明细失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||
// @AutoGenerate Version
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
||||
"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/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
"math"
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonConsumeAccountMonthBill struct{}
|
||||
|
||||
func NewCommonConsumeAccountMonthBill() *sCommonConsumeAccountMonthBill {
|
||||
return &sCommonConsumeAccountMonthBill{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonConsumeAccountMonthBill(NewCommonConsumeAccountMonthBill())
|
||||
}
|
||||
|
||||
// Model 消费账户月账单表ORM模型
|
||||
func (s *sCommonConsumeAccountMonthBill) 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.TConsumeAccountMonthBill.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取消费账户月账单表列表
|
||||
func (s *sCommonConsumeAccountMonthBill) List(ctx context.Context, in *model.TConsumeAccountMonthBillListParam) (list []*model.TConsumeAccountMonthBillListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TConsumeAccountMonthBillListModel{})
|
||||
|
||||
// 查询账单ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TConsumeAccountMonthBill.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询账单状态:1正常,2已调整,3已作废
|
||||
if in.BillStatus > 0 {
|
||||
mod = mod.Where(dao.TConsumeAccountMonthBill.Columns().BillStatus, in.BillStatus)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TConsumeAccountMonthBill.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TConsumeAccountMonthBill.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 *sCommonConsumeAccountMonthBill) Export(ctx context.Context, in *model.TConsumeAccountMonthBillListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TConsumeAccountMonthBillExportModel{})
|
||||
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.TConsumeAccountMonthBillExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增消费账户月账单表
|
||||
func (s *sCommonConsumeAccountMonthBill) Edit(ctx context.Context, in *model.TConsumeAccountMonthBillEditParam) (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.TConsumeAccountMonthBillUpdateFields{}).
|
||||
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.TConsumeAccountMonthBillInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增消费账户月账单表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除消费账户月账单表
|
||||
func (s *sCommonConsumeAccountMonthBill) Delete(ctx context.Context, in *model.TConsumeAccountMonthBillDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除消费账户月账单表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取消费账户月账单表指定信息
|
||||
func (s *sCommonConsumeAccountMonthBill) View(ctx context.Context, in *model.TConsumeAccountMonthBillViewParam) (res *model.TConsumeAccountMonthBillViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取消费账户月账单表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetSummary 获取月账单消费账户汇总信息
|
||||
func (s *sCommonConsumeAccountMonthBill) GetConsumptionBillSummary(ctx context.Context, month string) (res *model.TConsumptionBillSummaryModel, err error) {
|
||||
// 计算上月账期(基于当前时间)
|
||||
// now := time.Now()
|
||||
// lastMonthTime := now.AddDate(0, -1, 0)
|
||||
// lastMonthPeriod := lastMonthTime.Format("2006-01")
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
|
||||
var bill *model.TConsumeAccountMonthBillViewModel
|
||||
if err = dao.TConsumeAccountMonthBill.Ctx(ctx).
|
||||
Where(dao.TConsumeAccountMonthBill.Columns().AccountPeriod, month).
|
||||
Where(dao.TConsumeAccountMonthBill.Columns().DeptId, deptId).
|
||||
Scan(&bill); err != nil {
|
||||
err = gerror.Wrap(err, "查询上月账单失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if bill == nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = &model.TConsumptionBillSummaryModel{}
|
||||
res.TotalIncome = bill.TotalIncome
|
||||
res.TotalExpense = bill.TotalExpense
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SettleConsumeAccountMonthBill 按账期统计消费账户月账单。
|
||||
func (s *sCommonConsumeAccountMonthBill) SettleConsumeAccountMonthBill(ctx context.Context, in *model.TConsumeAccountMonthBillSettleParam) (res *model.TConsumeAccountMonthBillSettleResult, err error) {
|
||||
if in == nil {
|
||||
in = &model.TConsumeAccountMonthBillSettleParam{}
|
||||
}
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return nil, gerror.Wrap(err, "账期格式错误,请使用YYYY-MM")
|
||||
}
|
||||
if _, err = time.Parse("2006-01", in.AccountPeriod); err != nil {
|
||||
return nil, gerror.Wrap(err, "账期格式错误,请使用YYYY-MM")
|
||||
}
|
||||
|
||||
startTime, err := time.Parse("2006-01", in.AccountPeriod)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "账期格式错误,请使用YYYY-MM")
|
||||
}
|
||||
endTime := startTime.AddDate(0, 1, 0)
|
||||
now := gtime.Now()
|
||||
userId := contexts.GetUserId(ctx)
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
billColumns := dao.TConsumeAccountMonthBill.Columns()
|
||||
detailColumns := dao.TBalanceDetail.Columns()
|
||||
|
||||
type detailRow struct {
|
||||
Id int64 `orm:"id"`
|
||||
DeptId int64 `orm:"dept_id"`
|
||||
TenantId string `orm:"tenant_id"`
|
||||
CreatedDept int64 `orm:"created_dept"`
|
||||
IncomeExpenseType int `orm:"income_expense_type"`
|
||||
TransactionType int `orm:"transaction_type"`
|
||||
Amount float64 `orm:"amount"`
|
||||
BeforeBalance float64 `orm:"before_balance"`
|
||||
BalanceAfter float64 `orm:"balance_after"`
|
||||
TransactionTime *gtime.Time `orm:"transaction_time"`
|
||||
}
|
||||
|
||||
var rows []*detailRow
|
||||
mod := handler.Model(dao.TBalanceDetail.Ctx(ctx), &handler.Option{
|
||||
FilterTenant: false,
|
||||
FilterAuth: false,
|
||||
}).
|
||||
Fields(
|
||||
detailColumns.Id,
|
||||
detailColumns.DeptId,
|
||||
detailColumns.TenantId,
|
||||
detailColumns.CreatedDept,
|
||||
detailColumns.IncomeExpenseType,
|
||||
detailColumns.TransactionType,
|
||||
detailColumns.TransactionStatus,
|
||||
detailColumns.Amount,
|
||||
detailColumns.BeforeBalance,
|
||||
detailColumns.BalanceAfter,
|
||||
detailColumns.TransactionTime,
|
||||
).
|
||||
WhereGTE(detailColumns.TransactionTime, startTime).
|
||||
WhereLT(detailColumns.TransactionTime, endTime).
|
||||
Where(detailColumns.TransactionStatus, 1)
|
||||
if tenantId != "" {
|
||||
mod = mod.Where(detailColumns.TenantId, tenantId)
|
||||
}
|
||||
if err = mod.OrderAsc(detailColumns.TenantId).OrderAsc(detailColumns.DeptId).OrderAsc(detailColumns.TransactionTime).OrderAsc(detailColumns.Id).Scan(&rows); err != nil {
|
||||
return nil, gerror.Wrap(err, "查询消费流水失败,请稍后重试!")
|
||||
}
|
||||
|
||||
type billGroup struct {
|
||||
DeptId int64
|
||||
TenantId string
|
||||
CreatedDept int64
|
||||
AccountPeriod string
|
||||
OpeningBalance float64
|
||||
ClosingBalance float64
|
||||
RechargeIncome float64
|
||||
WithdrawableTransferInIncome float64
|
||||
ManualAdjustIncome float64
|
||||
RefundIncome float64
|
||||
ConsumeExpense float64
|
||||
BalanceTransferOutExpense float64
|
||||
ManualAdjustExpense float64
|
||||
ConsumeCreditRepayExpense float64
|
||||
TotalIncome float64
|
||||
TotalExpense float64
|
||||
IncomeCount int
|
||||
ExpenseCount int
|
||||
DetailCount int
|
||||
}
|
||||
|
||||
groups := make(map[string]*billGroup)
|
||||
keys := make([]string, 0, 16)
|
||||
for _, row := range rows {
|
||||
if row == nil || row.DeptId <= 0 || row.TransactionTime == nil {
|
||||
continue
|
||||
}
|
||||
switch row.TransactionType {
|
||||
case 0, 1, 2, 3, 4:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("%s:%d", row.TenantId, row.DeptId)
|
||||
group := groups[key]
|
||||
if group == nil {
|
||||
group = &billGroup{
|
||||
DeptId: row.DeptId,
|
||||
TenantId: row.TenantId,
|
||||
CreatedDept: row.CreatedDept,
|
||||
AccountPeriod: in.AccountPeriod,
|
||||
OpeningBalance: row.BeforeBalance,
|
||||
}
|
||||
groups[key] = group
|
||||
keys = append(keys, key)
|
||||
}
|
||||
group.ClosingBalance = row.BalanceAfter
|
||||
group.DetailCount++
|
||||
|
||||
switch row.TransactionType {
|
||||
case 0:
|
||||
if row.IncomeExpenseType == 0 {
|
||||
group.RechargeIncome += row.Amount
|
||||
}
|
||||
case 1:
|
||||
if row.IncomeExpenseType == 1 {
|
||||
group.ConsumeExpense += row.Amount
|
||||
}
|
||||
case 2:
|
||||
if row.IncomeExpenseType == 1 {
|
||||
group.BalanceTransferOutExpense += row.Amount
|
||||
}
|
||||
case 3:
|
||||
if row.IncomeExpenseType == 0 {
|
||||
group.WithdrawableTransferInIncome += row.Amount
|
||||
}
|
||||
case 4:
|
||||
if row.IncomeExpenseType == 0 {
|
||||
group.ManualAdjustIncome += row.Amount
|
||||
} else if row.IncomeExpenseType == 1 {
|
||||
group.ManualAdjustExpense += row.Amount
|
||||
}
|
||||
}
|
||||
|
||||
if row.IncomeExpenseType == 0 {
|
||||
group.TotalIncome += row.Amount
|
||||
group.IncomeCount++
|
||||
} else if row.IncomeExpenseType == 1 {
|
||||
group.TotalExpense += row.Amount
|
||||
group.ExpenseCount++
|
||||
}
|
||||
}
|
||||
|
||||
res = &model.TConsumeAccountMonthBillSettleResult{
|
||||
AccountPeriod: in.AccountPeriod,
|
||||
}
|
||||
if len(groups) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
for _, key := range keys {
|
||||
group := groups[key]
|
||||
if group == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
closingBalance := group.OpeningBalance + group.TotalIncome - group.TotalExpense
|
||||
if math.Abs(closingBalance-group.ClosingBalance) > 0.01 {
|
||||
return gerror.New(fmt.Sprintf("消费账单余额勾稽失败:部门 %d,账期 %s,计算余额 %.2f,流水余额 %.2f", group.DeptId, group.AccountPeriod, closingBalance, group.ClosingBalance))
|
||||
}
|
||||
|
||||
lookup := tx.Ctx(ctx).Model(dao.TConsumeAccountMonthBill.Table()).
|
||||
Fields(billColumns.Id).
|
||||
Where(billColumns.DeptId, group.DeptId).
|
||||
Where(billColumns.AccountPeriod, group.AccountPeriod)
|
||||
if group.TenantId != "" {
|
||||
lookup = lookup.Where(billColumns.TenantId, group.TenantId)
|
||||
}
|
||||
|
||||
var existing struct {
|
||||
Id int64 `orm:"id"`
|
||||
}
|
||||
if err := lookup.LockUpdate().Scan(&existing); err != nil && err != sql.ErrNoRows {
|
||||
return gerror.Wrap(err, "查询消费月账单失败,请稍后重试!")
|
||||
}
|
||||
|
||||
tenantValue := group.TenantId
|
||||
if tenantValue == "" {
|
||||
tenantValue = tenantId
|
||||
}
|
||||
if tenantValue == "" {
|
||||
return gerror.New("租户ID不能为空")
|
||||
}
|
||||
|
||||
createdDeptValue := group.CreatedDept
|
||||
if createdDeptValue == 0 {
|
||||
createdDeptValue = contexts.GetDeptId(ctx)
|
||||
}
|
||||
if createdDeptValue == 0 {
|
||||
return gerror.New("创建部门不能为空")
|
||||
}
|
||||
|
||||
data := g.Map{
|
||||
billColumns.DeptId: group.DeptId,
|
||||
billColumns.AccountPeriod: group.AccountPeriod,
|
||||
billColumns.OpeningBalance: group.OpeningBalance,
|
||||
billColumns.RechargeIncome: group.RechargeIncome,
|
||||
billColumns.WithdrawableTransferInIncome: group.WithdrawableTransferInIncome,
|
||||
billColumns.ManualAdjustIncome: group.ManualAdjustIncome,
|
||||
billColumns.RefundIncome: 0,
|
||||
billColumns.ConsumeExpense: group.ConsumeExpense,
|
||||
billColumns.BalanceTransferOutExpense: group.BalanceTransferOutExpense,
|
||||
billColumns.ManualAdjustExpense: group.ManualAdjustExpense,
|
||||
billColumns.ConsumeCreditRepayExpense: group.ConsumeCreditRepayExpense,
|
||||
billColumns.TotalIncome: group.TotalIncome,
|
||||
billColumns.TotalExpense: group.TotalExpense,
|
||||
billColumns.ClosingBalance: closingBalance,
|
||||
billColumns.IncomeCount: group.IncomeCount,
|
||||
billColumns.ExpenseCount: group.ExpenseCount,
|
||||
billColumns.CreditUsed: 0,
|
||||
billColumns.CreditRepaid: 0,
|
||||
billColumns.BillStatus: 1,
|
||||
billColumns.SettleTime: now,
|
||||
billColumns.Remark: "根据余额明细统计生成",
|
||||
billColumns.TenantId: tenantValue,
|
||||
billColumns.UpdatedBy: userId,
|
||||
billColumns.UpdatedAt: now,
|
||||
}
|
||||
|
||||
if existing.Id > 0 {
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TConsumeAccountMonthBill.Table()).
|
||||
WherePri(existing.Id).
|
||||
Data(data).
|
||||
Update(); err != nil {
|
||||
return gerror.Wrap(err, "更新消费月账单失败,请稍后重试!")
|
||||
}
|
||||
res.UpdateCount++
|
||||
} else {
|
||||
data[billColumns.CreatedDept] = createdDeptValue
|
||||
data[billColumns.CreatedBy] = userId
|
||||
data[billColumns.CreatedAt] = now
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TConsumeAccountMonthBill.Table()).
|
||||
Data(data).
|
||||
Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增消费月账单失败,请稍后重试!")
|
||||
}
|
||||
res.InsertCount++
|
||||
}
|
||||
|
||||
res.DeptCount++
|
||||
res.DetailCount += group.DetailCount
|
||||
res.TotalIncome += group.TotalIncome
|
||||
res.TotalExpense += group.TotalExpense
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/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/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTCreditDetail struct{}
|
||||
|
||||
func NewCommonTCreditDetail() *sCommonTCreditDetail {
|
||||
return &sCommonTCreditDetail{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTCreditDetail(NewCommonTCreditDetail())
|
||||
}
|
||||
|
||||
// Model 信用额度明细表ORM模型
|
||||
func (s *sCommonTCreditDetail) 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.TCreditDetail.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取信用额度明细表列表
|
||||
func (s *sCommonTCreditDetail) List(ctx context.Context, in *model.TCreditDetailListParam) (list []*model.TCreditDetailListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TCreditDetailListModel{})
|
||||
|
||||
// 查询明细ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TCreditDetail.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TCreditDetail.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TCreditDetail.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 *sCommonTCreditDetail) Export(ctx context.Context, in *model.TCreditDetailListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TCreditDetailExportModel{})
|
||||
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.TCreditDetailExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增信用额度明细表
|
||||
func (s *sCommonTCreditDetail) Edit(ctx context.Context, in *model.TCreditDetailEditParam) (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.TCreditDetailUpdateFields{}).
|
||||
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.TCreditDetailInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增信用额度明细表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除信用额度明细表
|
||||
func (s *sCommonTCreditDetail) Delete(ctx context.Context, in *model.TCreditDetailDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除信用额度明细表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取信用额度明细表指定信息
|
||||
func (s *sCommonTCreditDetail) View(ctx context.Context, in *model.TCreditDetailViewParam) (res *model.TCreditDetailViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取信用额度明细表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// Package iot
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/xgorm/handler"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
"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"
|
||||
|
||||
"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/util/gconv"
|
||||
)
|
||||
|
||||
type sIotTDivisionCode struct{}
|
||||
|
||||
func NewIotTDivisionCode() *sIotTDivisionCode {
|
||||
return &sIotTDivisionCode{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterIotTDivisionCode(NewIotTDivisionCode())
|
||||
}
|
||||
|
||||
// Model 村级行政区划表ORM模型
|
||||
func (s *sIotTDivisionCode) 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.TDivisionCode.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取村级行政区划表列表
|
||||
func (s *sIotTDivisionCode) List(ctx context.Context, in *model.TDivisionCodeListParam) (list []*model.TDivisionCodeListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 根据父级代码查询子级列表
|
||||
if in.RegionParent != "" {
|
||||
mod = mod.Where(dao.TDivisionCode.Columns().RegionParent, in.RegionParent)
|
||||
}
|
||||
|
||||
// 根据名称查询
|
||||
if in.RegionName != "" {
|
||||
mod = mod.WhereLike(dao.TDivisionCode.Columns().RegionName, "%"+in.RegionName+"%")
|
||||
}
|
||||
|
||||
// 根据代码查询
|
||||
if in.RegionCode != "" {
|
||||
mod = mod.Where(dao.TDivisionCode.Columns().RegionCode, in.RegionCode)
|
||||
}
|
||||
|
||||
// 根据等级查询
|
||||
if in.RegionLevel > 0 {
|
||||
mod = mod.Where(dao.TDivisionCode.Columns().RegionLevel, in.RegionLevel)
|
||||
}
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TDivisionCodeListModel{})
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TDivisionCode.Columns().RegionId)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取村级行政区划表列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出村级行政区划表
|
||||
func (s *sIotTDivisionCode) Export(ctx context.Context, in *model.TDivisionCodeListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TDivisionCodeExportModel{})
|
||||
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.TDivisionCodeExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增村级行政区划表
|
||||
func (s *sIotTDivisionCode) Edit(ctx context.Context, in *model.TDivisionCodeEditParam) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.RegionId > 0 {
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TDivisionCodeUpdateFields{}).
|
||||
WherePri(in.RegionId).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改村级行政区划表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(model.TDivisionCodeInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增村级行政区划表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除村级行政区划表
|
||||
func (s *sIotTDivisionCode) Delete(ctx context.Context, in *model.TDivisionCodeDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.RegionId).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除村级行政区划表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取村级行政区划表指定信息
|
||||
func (s *sIotTDivisionCode) View(ctx context.Context, in *model.TDivisionCodeViewParam) (res *model.TDivisionCodeViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.RegionId).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取村级行政区划表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
||||
"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/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
"strconv"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTFinCommissionDetail struct{}
|
||||
|
||||
func NewCommonTFinCommissionDetail() *sCommonTFinCommissionDetail {
|
||||
return &sCommonTFinCommissionDetail{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTFinCommissionDetail(NewCommonTFinCommissionDetail())
|
||||
}
|
||||
|
||||
// Model 分润明细表ORM模型
|
||||
func (s *sCommonTFinCommissionDetail) 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.TFinCommissionDetail.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取分润明细表列表
|
||||
func (s *sCommonTFinCommissionDetail) List(ctx context.Context, in *model.TFinCommissionDetailListParam) (list []*model.TFinCommissionDetailListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TFinCommissionDetailListModel{})
|
||||
|
||||
// 查询主键ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TFinCommissionDetail.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询分润状态:0待结算,1可提现
|
||||
if in.CommissionStatus > 0 {
|
||||
mod = mod.Where(dao.TFinCommissionDetail.Columns().CommissionStatus, in.CommissionStatus)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TFinCommissionDetail.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TFinCommissionDetail.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 *sCommonTFinCommissionDetail) Export(ctx context.Context, in *model.TFinCommissionDetailListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TFinCommissionDetailExportModel{})
|
||||
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.TFinCommissionDetailExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增分润明细表
|
||||
func (s *sCommonTFinCommissionDetail) Edit(ctx context.Context, in *model.TFinCommissionDetailEditParam) (err error) {
|
||||
// 验证'CommissionNo'唯一
|
||||
if err = xgorm.IsUnique(ctx, &dao.TFinCommissionDetail, g.Map{dao.TFinCommissionDetail.Columns().CommissionNo: in.CommissionNo}, "分润明细编号已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
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.TFinCommissionDetailUpdateFields{}).
|
||||
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.TFinCommissionDetailInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增分润明细表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除分润明细表
|
||||
func (s *sCommonTFinCommissionDetail) Delete(ctx context.Context, in *model.TFinCommissionDetailDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除分润明细表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取分润明细表指定信息
|
||||
func (s *sCommonTFinCommissionDetail) View(ctx context.Context, in *model.TFinCommissionDetailViewParam) (res *model.TFinCommissionDetailViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取分润明细表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarkSettled 批量标记分润明细为可提现状态。
|
||||
func (s *sCommonTFinCommissionDetail) MarkSettled(ctx context.Context, tx gdb.TX, in *model.TFinCommissionSettleUpdateParam) error {
|
||||
if in == nil {
|
||||
return gerror.New("分润结算更新参数不能为空")
|
||||
}
|
||||
if len(in.Ids) == 0 {
|
||||
return gerror.New("分润明细ID不能为空")
|
||||
}
|
||||
settlementTime := in.SettlementTime
|
||||
if settlementTime == nil {
|
||||
settlementTime = gtime.Now()
|
||||
}
|
||||
userId := contexts.GetUserId(ctx)
|
||||
columns := dao.TFinCommissionDetail.Columns()
|
||||
result, err := tx.Ctx(ctx).Model(dao.TFinCommissionDetail.Table()).
|
||||
WhereIn(columns.Id, in.Ids).
|
||||
Where(columns.CommissionStatus, financeCommissionStatusPending).
|
||||
Data(g.Map{
|
||||
columns.CommissionStatus: financeCommissionStatusSettled,
|
||||
columns.SettlementTime: settlementTime,
|
||||
columns.UpdatedBy: userId,
|
||||
columns.UpdatedAt: settlementTime,
|
||||
}).
|
||||
Update()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "更新分润明细结算状态失败,请稍后重试!")
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "获取分润明细结算状态更新结果失败")
|
||||
}
|
||||
if affected != int64(len(in.Ids)) {
|
||||
return gerror.New("部分分润明细状态已变化,请下次任务重试")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 分层分润概览 分润表
|
||||
// 一级客户收入 benefit_dept_level=1 commission_amount相加
|
||||
// 二级客户收入 benefit_dept_level=2 commission_amount相加
|
||||
// 三级客户收入 benefit_dept_level=3 commission_amount相加
|
||||
// 总收入 一级二级三级加起来总和
|
||||
func (s *sCommonTFinCommissionDetail) GetCommissionOverview(ctx context.Context, in *model.TCommissionOverviewParam) (res *model.TCommissionOverviewModel, err error) {
|
||||
res = &model.TCommissionOverviewModel{}
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
|
||||
// 解析账期参数
|
||||
var startPeriod, endPeriod string
|
||||
if in.Type == 1 { // 年
|
||||
startPeriod = in.Year + "-01"
|
||||
endPeriod = in.Year + "-12"
|
||||
} else if in.Type == 2 { // 月
|
||||
startPeriod = in.Month
|
||||
endPeriod = in.Month
|
||||
}
|
||||
|
||||
// 查询一级客户收入
|
||||
type levelRow struct {
|
||||
CommissionAmount float64 `orm:"commission_amount"`
|
||||
}
|
||||
var level1Result *levelRow
|
||||
if err = handler.Model(dao.TFinCommissionDetail.Ctx(ctx)).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptId, deptId).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptLevel, 1).
|
||||
WhereGTE(dao.TFinCommissionDetail.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TFinCommissionDetail.Columns().AccountPeriod, endPeriod).
|
||||
Fields("SUM(" + dao.TFinCommissionDetail.Columns().CommissionAmount + ") as commission_amount").
|
||||
Scan(&level1Result); err != nil {
|
||||
err = gerror.Wrap(err, "查询一级客户收入失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if level1Result != nil {
|
||||
res.Level1Income = level1Result.CommissionAmount
|
||||
}
|
||||
|
||||
// 查询二级客户收入
|
||||
var level2Result *levelRow
|
||||
if err = handler.Model(dao.TFinCommissionDetail.Ctx(ctx)).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptId, deptId).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptLevel, 2).
|
||||
WhereGTE(dao.TFinCommissionDetail.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TFinCommissionDetail.Columns().AccountPeriod, endPeriod).
|
||||
Fields("SUM(" + dao.TFinCommissionDetail.Columns().CommissionAmount + ") as commission_amount").
|
||||
Scan(&level2Result); err != nil {
|
||||
err = gerror.Wrap(err, "查询二级客户收入失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if level2Result != nil {
|
||||
res.Level2Income = level2Result.CommissionAmount
|
||||
}
|
||||
|
||||
// 查询三级客户收入
|
||||
var level3Result *levelRow
|
||||
if err = handler.Model(dao.TFinCommissionDetail.Ctx(ctx)).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptId, deptId).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptLevel, 3).
|
||||
WhereGTE(dao.TFinCommissionDetail.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TFinCommissionDetail.Columns().AccountPeriod, endPeriod).
|
||||
Fields("SUM(" + dao.TFinCommissionDetail.Columns().CommissionAmount + ") as commission_amount").
|
||||
Scan(&level3Result); err != nil {
|
||||
err = gerror.Wrap(err, "查询三级客户收入失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if level3Result != nil {
|
||||
res.Level3Income = level3Result.CommissionAmount
|
||||
}
|
||||
|
||||
// 计算总收入
|
||||
res.TotalIncome = res.Level1Income + res.Level2Income + res.Level3Income
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetLevelSummary 获取层级汇总表
|
||||
// 客户数 根据 benefit_dept_level 收益机构层级分组
|
||||
// 收入金额 根据收益机构id去 t_profit_account_month_bill 查,dept_id=收益机构id,收入=分润结算+余额转提现
|
||||
// 分润金额 根据收益机构id去 t_profit_account_month_bill 查,commission_income
|
||||
// 顶级利润贡献 等于分润金额
|
||||
// 收入占比
|
||||
// 环比 选年与去年对比,选月与上个月对比
|
||||
func (s *sCommonTFinCommissionDetail) GetLevelSummary(ctx context.Context, in *model.TLevelSummaryParam) (res *model.TLevelSummaryModel, err error) {
|
||||
res = &model.TLevelSummaryModel{}
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
|
||||
// 解析账期参数
|
||||
var startPeriod, endPeriod string
|
||||
if in.Type == 1 { // 年
|
||||
startPeriod = in.Year + "-01"
|
||||
endPeriod = in.Year + "-12"
|
||||
} else if in.Type == 2 { // 月
|
||||
startPeriod = in.Month
|
||||
endPeriod = in.Month
|
||||
}
|
||||
|
||||
// 查询各层级的客户数和分润金额(从分润表)
|
||||
type levelData struct {
|
||||
Level int `orm:"level"`
|
||||
CustomerCount int `orm:"customer_count"`
|
||||
CommissionAmount float64 `orm:"commission_amount"`
|
||||
}
|
||||
var levelResults []*levelData
|
||||
if err = handler.Model(dao.TFinCommissionDetail.Ctx(ctx)).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptId, deptId).
|
||||
WhereGTE(dao.TFinCommissionDetail.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TFinCommissionDetail.Columns().AccountPeriod, endPeriod).
|
||||
Fields("SUM(" + dao.TFinCommissionDetail.Columns().CommissionAmount + ") as commission_amount, COUNT(DISTINCT " + dao.TFinCommissionDetail.Columns().PayerDeptId + ") as customer_count, " + dao.TFinCommissionDetail.Columns().BenefitDeptLevel + " as level").
|
||||
Group(dao.TFinCommissionDetail.Columns().BenefitDeptLevel).
|
||||
Scan(&levelResults); err != nil {
|
||||
err = gerror.Wrap(err, "查询层级数据失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
// 构建层级与分润金额的映射
|
||||
levelCommissionMap := make(map[int]float64)
|
||||
for _, lr := range levelResults {
|
||||
levelCommissionMap[lr.Level] = lr.CommissionAmount
|
||||
}
|
||||
|
||||
// 查询各层级的收入金额(从盈利月账单表:total_income)
|
||||
// 先从分润表获取各层级的机构ID列表
|
||||
type deptLevelRow struct {
|
||||
Level int `orm:"level"`
|
||||
DeptId int64 `orm:"dept_id"`
|
||||
}
|
||||
var deptLevelResults []*deptLevelRow
|
||||
if err = handler.Model(dao.TFinCommissionDetail.Ctx(ctx)).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptId, deptId).
|
||||
WhereGTE(dao.TFinCommissionDetail.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TFinCommissionDetail.Columns().AccountPeriod, endPeriod).
|
||||
Fields("DISTINCT " + dao.TFinCommissionDetail.Columns().BenefitDeptLevel + " as level, " + dao.TFinCommissionDetail.Columns().BenefitDeptId + " as dept_id").
|
||||
Scan(&deptLevelResults); err != nil {
|
||||
err = gerror.Wrap(err, "查询层级机构失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
// 根据层级汇总机构ID
|
||||
levelDeptMap := make(map[int][]int64)
|
||||
for _, dlr := range deptLevelResults {
|
||||
levelDeptMap[dlr.Level] = append(levelDeptMap[dlr.Level], dlr.DeptId)
|
||||
}
|
||||
|
||||
// 构建收入金额映射
|
||||
incomeMap := make(map[int]float64)
|
||||
for level, deptIds := range levelDeptMap {
|
||||
if len(deptIds) == 0 {
|
||||
continue
|
||||
}
|
||||
incomeResult, err := s.getProfitAccountIncome(ctx, &model.IncomeQueryParams{
|
||||
DeptIds: deptIds,
|
||||
StartPeriod: startPeriod,
|
||||
EndPeriod: endPeriod,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
incomeMap[level] = incomeResult
|
||||
}
|
||||
|
||||
// 计算总收入
|
||||
var totalIncome float64
|
||||
for _, item := range levelResults {
|
||||
totalIncome += item.CommissionAmount
|
||||
}
|
||||
|
||||
// 构建结果
|
||||
for level := 1; level <= 3; level++ {
|
||||
item := model.TLevelSummaryItem{
|
||||
Level: level,
|
||||
}
|
||||
|
||||
// 设置客户数、分润金额
|
||||
for _, lr := range levelResults {
|
||||
if lr.Level == level {
|
||||
item.CustomerCount = lr.CustomerCount
|
||||
item.CommissionAmount = lr.CommissionAmount
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 设置收入金额
|
||||
item.IncomeAmount = incomeMap[level]
|
||||
|
||||
// 计算收入占比
|
||||
if totalIncome > 0 {
|
||||
item.IncomeRatio = item.CommissionAmount / totalIncome * 100
|
||||
}
|
||||
|
||||
res.Items = append(res.Items, item)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getProfitAccountIncome 查询盈利账户收入
|
||||
func (s *sCommonTFinCommissionDetail) getProfitAccountIncome(ctx context.Context, params *model.IncomeQueryParams) (float64, error) {
|
||||
type incomeRow struct {
|
||||
TotalIncome float64 `orm:"total_income"`
|
||||
}
|
||||
var row *incomeRow
|
||||
if err := handler.Model(dao.TProfitAccountMonthBill.Ctx(ctx)).
|
||||
WhereIn(dao.TProfitAccountMonthBill.Columns().DeptId, params.DeptIds).
|
||||
WhereGTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, params.StartPeriod).
|
||||
WhereLTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, params.EndPeriod).
|
||||
Fields("SUM(" + dao.TProfitAccountMonthBill.Columns().TotalIncome + ") as total_income").
|
||||
Scan(&row); err != nil {
|
||||
return 0, gerror.Wrap(err, "查询盈利账户收入失败")
|
||||
}
|
||||
if row == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return row.TotalIncome, nil
|
||||
}
|
||||
|
||||
// mustAtoi 安全转换字符串为int
|
||||
func mustAtoi(s string) int {
|
||||
i, _ := strconv.Atoi(s)
|
||||
return i
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/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/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTFinWithdrawableChange struct{}
|
||||
|
||||
func NewCommonTFinWithdrawableChange() *sCommonTFinWithdrawableChange {
|
||||
return &sCommonTFinWithdrawableChange{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTFinWithdrawableChange(NewCommonTFinWithdrawableChange())
|
||||
}
|
||||
|
||||
// Model 可提现变动明细表ORM模型
|
||||
func (s *sCommonTFinWithdrawableChange) 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.TFinWithdrawableChange.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取可提现变动明细表列表
|
||||
func (s *sCommonTFinWithdrawableChange) List(ctx context.Context, in *model.TFinWithdrawableChangeListParam) (list []*model.TFinWithdrawableChangeListModel, totalCount int, err error) {
|
||||
|
||||
mod := dao.TFinWithdrawableChange.Ctx(ctx)
|
||||
sysDeptTable := dao.SysDept.Table()
|
||||
orderTable := dao.TOrder.Table()
|
||||
// mod = mod.FieldsPrefix(dao.TSimCardInfo.Table(), model.TSimCardInfoListModel{})
|
||||
// mod = mod.Fields(simChannels + ".chan_name as chanName, chan_alias as chanAlias")
|
||||
|
||||
mod = mod.FieldsPrefix(dao.TFinWithdrawableChange.Table(), model.TFinWithdrawableChangeListModel{})
|
||||
mod = mod.Fields(sysDeptTable + ".dept_name as deptName")
|
||||
mod = mod.Fields(orderTable + ".order_type as orderType")
|
||||
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Where(dao.TFinWithdrawableChange.Columns().DeptId, deptId)
|
||||
|
||||
// 查询主键ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TFinWithdrawableChange.Columns().Id, in.Id)
|
||||
}
|
||||
// 查询月份范围(根据 account_period 字段)
|
||||
if len(in.Month) == 2 {
|
||||
mod = mod.WhereGTE(dao.TFinWithdrawableChange.Columns().AccountPeriod, in.Month[0])
|
||||
mod = mod.WhereLTE(dao.TFinWithdrawableChange.Columns().AccountPeriod, in.Month[1])
|
||||
}
|
||||
// 查询收支类型
|
||||
if in.IncomeExpenseType != nil {
|
||||
mod = mod.Where(dao.TFinWithdrawableChange.Columns().IncomeExpenseType, *in.IncomeExpenseType)
|
||||
}
|
||||
// 查询变动类型
|
||||
if in.ChangeType != nil {
|
||||
mod = mod.Where(dao.TFinWithdrawableChange.Columns().ChangeType, *in.ChangeType)
|
||||
}
|
||||
// 查询账期
|
||||
if in.AccountPeriod != "" {
|
||||
mod = mod.Where(dao.TFinWithdrawableChange.Columns().AccountPeriod, in.AccountPeriod)
|
||||
}
|
||||
|
||||
mod = mod.LeftJoin(sysDeptTable, dao.TFinWithdrawableChange.Table()+"."+dao.TFinWithdrawableChange.Columns().DeptId+" COLLATE utf8mb4_general_ci="+sysDeptTable+"."+dao.SysDept.Columns().DeptId)
|
||||
mod = mod.LeftJoin(orderTable, dao.TFinWithdrawableChange.Table()+"."+dao.TFinWithdrawableChange.Columns().SourceNo+" COLLATE utf8mb4_general_ci="+orderTable+"."+dao.TOrder.Columns().OrderSn)
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TFinWithdrawableChange.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取可提现变动明细表列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
// 根据 TransactionTime 赋值账期
|
||||
for i := range list {
|
||||
if list[i].CreatedAt != nil {
|
||||
list[i].TradingMonth = list[i].CreatedAt.Format("Y-m")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出可提现变动明细表
|
||||
func (s *sCommonTFinWithdrawableChange) Export(ctx context.Context, in *model.TFinWithdrawableChangeListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TFinWithdrawableChangeExportModel{})
|
||||
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.TFinWithdrawableChangeExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增可提现变动明细表
|
||||
func (s *sCommonTFinWithdrawableChange) Edit(ctx context.Context, in *model.TFinWithdrawableChangeEditParam) (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.TFinWithdrawableChangeUpdateFields{}).
|
||||
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.TFinWithdrawableChangeInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增可提现变动明细表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除可提现变动明细表
|
||||
func (s *sCommonTFinWithdrawableChange) Delete(ctx context.Context, in *model.TFinWithdrawableChangeDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除可提现变动明细表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取可提现变动明细表指定信息
|
||||
func (s *sCommonTFinWithdrawableChange) View(ctx context.Context, in *model.TFinWithdrawableChangeViewParam) (res *model.TFinWithdrawableChangeViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取可提现变动明细表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateBalanceTransferInChange 写入余额转可提现审核通过后的可提现金额变动明细。
|
||||
func (s *sCommonTFinWithdrawableChange) CreateBalanceTransferInChange(ctx context.Context, tx gdb.TX, in *model.TFinWithdrawableBalanceTransferInParam) error {
|
||||
if in == nil {
|
||||
return gerror.New("可提现变动明细参数不能为空")
|
||||
}
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
userId := contexts.GetUserId(ctx)
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
createdAt := gtime.Now()
|
||||
columns := dao.TFinWithdrawableChange.Columns()
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TFinWithdrawableChange.Table()).Data(g.Map{
|
||||
columns.DeptId: in.DeptId,
|
||||
columns.ChangeType: financeWithdrawableChangeTypeBalanceTransferIn,
|
||||
columns.IncomeExpenseType: financeIncomeExpenseIncome,
|
||||
columns.ChangeAmount: in.ChangeAmount,
|
||||
columns.BeforeBalance: in.BeforeBalance,
|
||||
columns.AfterBalance: in.AfterBalance,
|
||||
columns.AccountType: in.AccountType,
|
||||
columns.BankName: in.BankName,
|
||||
columns.BankAccount: in.BankAccount,
|
||||
columns.AccountName: in.AccountName,
|
||||
columns.IdentityCard: in.IdentityCard,
|
||||
columns.SourceNo: in.SourceNo,
|
||||
columns.Remark: in.Remark,
|
||||
columns.AccountPeriod: in.AccountPeriod,
|
||||
columns.TenantId: tenantId,
|
||||
columns.CreatedDept: deptId,
|
||||
columns.CreatedBy: userId,
|
||||
columns.CreatedAt: createdAt,
|
||||
}).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增可提现变动明细失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateCommissionSettlementChange 写入分润结算产生的可提现明细。
|
||||
func (s *sCommonTFinWithdrawableChange) CreateCommissionSettlementChange(ctx context.Context, tx gdb.TX, in *model.TFinWithdrawableCommissionSettlementParam) error {
|
||||
if in == nil {
|
||||
return gerror.New("分润结算可提现明细参数不能为空")
|
||||
}
|
||||
if in.DeptId <= 0 {
|
||||
return gerror.New("机构ID不能为空")
|
||||
}
|
||||
if in.ChangeAmount <= 0 {
|
||||
return gerror.New("分润结算金额必须大于0")
|
||||
}
|
||||
if in.AccountPeriod == "" {
|
||||
return gerror.New("账期不能为空")
|
||||
}
|
||||
if in.SourceNo == "" {
|
||||
return gerror.New("结算批次号不能为空")
|
||||
}
|
||||
|
||||
tenantId := in.TenantId
|
||||
if tenantId == "" {
|
||||
tenantId = contexts.GetTenantId(ctx)
|
||||
}
|
||||
userId := in.CreatedBy
|
||||
if userId <= 0 {
|
||||
userId = contexts.GetUserId(ctx)
|
||||
}
|
||||
deptId := in.CreatedDept
|
||||
if deptId <= 0 {
|
||||
deptId = contexts.GetDeptId(ctx)
|
||||
}
|
||||
if deptId <= 0 {
|
||||
deptId = in.DeptId
|
||||
}
|
||||
createdAt := in.CreatedAt
|
||||
if createdAt == nil {
|
||||
createdAt = gtime.Now()
|
||||
}
|
||||
|
||||
columns := dao.TFinWithdrawableChange.Columns()
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TFinWithdrawableChange.Table()).Data(g.Map{
|
||||
columns.DeptId: in.DeptId,
|
||||
columns.ChangeType: financeWithdrawableChangeTypeCommission,
|
||||
columns.IncomeExpenseType: financeIncomeExpenseIncome,
|
||||
columns.ChangeAmount: in.ChangeAmount,
|
||||
columns.BeforeBalance: in.BeforeBalance,
|
||||
columns.AfterBalance: in.AfterBalance,
|
||||
columns.AccountType: 0,
|
||||
columns.SourceNo: in.SourceNo,
|
||||
columns.Remark: in.Remark,
|
||||
columns.AccountPeriod: in.AccountPeriod,
|
||||
columns.TenantId: tenantId,
|
||||
columns.CreatedDept: deptId,
|
||||
columns.CreatedBy: userId,
|
||||
columns.CreatedAt: createdAt,
|
||||
}).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增分润结算可提现明细失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
||||
"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"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTFinanceApply struct{}
|
||||
|
||||
func NewCommonTFinanceApply() *sCommonTFinanceApply {
|
||||
return &sCommonTFinanceApply{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTFinanceApply(NewCommonTFinanceApply())
|
||||
}
|
||||
|
||||
// Model 财务申请表ORM模型
|
||||
func (s *sCommonTFinanceApply) 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.TFinanceApply.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取财务申请表列表
|
||||
func (s *sCommonTFinanceApply) List(ctx context.Context, in *model.TFinanceApplyListParam) (list []*model.TFinanceApplyListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
applyTable := dao.TFinanceApply.Table()
|
||||
applyDeptAlias := "apply_dept"
|
||||
auditorDeptAlias := "auditor_dept"
|
||||
sysDeptTable := dao.SysDept.Table()
|
||||
mod = mod.LeftJoin(sysDeptTable+" as "+applyDeptAlias, applyDeptAlias+"."+dao.SysDept.Columns().DeptId+"="+applyTable+"."+dao.TFinanceApply.Columns().ApplyDeptId)
|
||||
mod = mod.LeftJoin(sysDeptTable+" as "+auditorDeptAlias, auditorDeptAlias+"."+dao.SysDept.Columns().DeptId+"="+applyTable+"."+dao.TFinanceApply.Columns().AuditDeptId)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.FieldsPrefix(applyTable, model.TFinanceApplyListModel{})
|
||||
mod = mod.Fields(applyDeptAlias + ".dept_name as applyDeptName")
|
||||
mod = mod.Fields(auditorDeptAlias + ".dept_name as auditDeptName")
|
||||
|
||||
// 查询主键ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TFinanceApply.Columns().Id, in.Id)
|
||||
}
|
||||
if in.ApplyNo != nil {
|
||||
mod = mod.Where(dao.TFinanceApply.Columns().ApplyNo, *in.ApplyNo)
|
||||
}
|
||||
|
||||
// 查询状态:0待审核,1已通过,2已驳回,3已打款,4已取消
|
||||
if in.ApplyStatus != nil {
|
||||
mod = mod.Where(dao.TFinanceApply.Columns().ApplyStatus, *in.ApplyStatus)
|
||||
}
|
||||
if in.ApplyType != nil {
|
||||
mod = mod.Where(dao.TFinanceApply.Columns().ApplyType, *in.ApplyType)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TFinanceApply.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TFinanceApply.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 *sCommonTFinanceApply) Export(ctx context.Context, in *model.TFinanceApplyListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TFinanceApplyExportModel{})
|
||||
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.TFinanceApplyExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增财务申请表
|
||||
func (s *sCommonTFinanceApply) Edit(ctx context.Context, in *model.TFinanceApplyEditParam) (err error) {
|
||||
// 验证'ApplyNo'唯一
|
||||
if err = xgorm.IsUnique(ctx, &dao.TFinanceApply, g.Map{dao.TFinanceApply.Columns().ApplyNo: in.ApplyNo}, "申请单号已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
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()
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TFinanceApplyUpdateFields{}).
|
||||
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.TFinanceApplyInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增财务申请表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除财务申请表
|
||||
func (s *sCommonTFinanceApply) Delete(ctx context.Context, in *model.TFinanceApplyDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除财务申请表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取财务申请表指定信息
|
||||
func (s *sCommonTFinanceApply) View(ctx context.Context, in *model.TFinanceApplyViewParam) (res *model.TFinanceApplyViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取财务申请表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateBalanceTransferApply 创建余额转可提现的待审核申请单。
|
||||
func (s *sCommonTFinanceApply) CreateBalanceTransferApply(ctx context.Context, tx gdb.TX, in *model.TFinanceApplyCreateBalanceTransferParam) error {
|
||||
if in == nil {
|
||||
return gerror.New("财务申请参数不能为空")
|
||||
}
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
userId := contexts.GetUserId(ctx)
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
createdAt := gtime.Now()
|
||||
columns := dao.TFinanceApply.Columns()
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TFinanceApply.Table()).Data(g.Map{
|
||||
columns.ApplyNo: in.ApplyNo,
|
||||
columns.ApplyType: financeApplyTypeBalanceToWithdraw,
|
||||
columns.AuditType: financeAuditTypeFirst,
|
||||
columns.ApplyDeptId: in.ApplyDeptId,
|
||||
columns.AuditDeptId: in.AuditDeptId,
|
||||
columns.AvailableAmountSnapshot: in.AvailableAmountSnapshot,
|
||||
columns.ApplyAmount: in.ApplyAmount,
|
||||
columns.AccountType: 0,
|
||||
columns.ApplyStatus: financeApplyStatusPending,
|
||||
columns.ApplyTime: createdAt,
|
||||
columns.Remark: in.Remark,
|
||||
columns.TenantId: tenantId,
|
||||
columns.CreatedDept: deptId,
|
||||
columns.CreatedBy: userId,
|
||||
columns.CreatedAt: createdAt,
|
||||
}).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增余额转可提现申请失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPendingByApplyNo 按申请单号查询待审核申请。
|
||||
func (s *sCommonTFinanceApply) GetPendingByApplyNo(ctx context.Context, tx gdb.TX, applyNo string, tenantId string) (*entity.TFinanceApply, error) {
|
||||
apply := &entity.TFinanceApply{}
|
||||
columns := dao.TFinanceApply.Columns()
|
||||
if err := tx.Ctx(ctx).Model(dao.TFinanceApply.Table()).
|
||||
Where(columns.ApplyNo, applyNo).
|
||||
Where(columns.ApplyStatus, financeApplyStatusPending).
|
||||
Where(columns.TenantId, tenantId).
|
||||
Scan(apply); err != nil {
|
||||
return nil, gerror.Wrap(err, "查询财务申请失败,请稍后重试!")
|
||||
}
|
||||
if apply.Id == 0 {
|
||||
return nil, gerror.New("财务申请不存在或已审核")
|
||||
}
|
||||
return apply, nil
|
||||
}
|
||||
|
||||
// UpdateAuditStatus 更新财务申请的审核状态。
|
||||
func (s *sCommonTFinanceApply) UpdateAuditStatus(ctx context.Context, tx gdb.TX, in *model.TFinanceApplyUpdateAuditStatusParam) error {
|
||||
if in == nil {
|
||||
return gerror.New("财务申请审核状态参数不能为空")
|
||||
}
|
||||
userId := contexts.GetUserId(ctx)
|
||||
updatedAt := gtime.Now()
|
||||
columns := dao.TFinanceApply.Columns()
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TFinanceApply.Table()).
|
||||
Where(columns.ApplyNo, in.ApplyNo).
|
||||
Data(g.Map{
|
||||
columns.ApplyStatus: in.ApplyStatus,
|
||||
columns.AuditTime: in.AuditTime,
|
||||
columns.UpdatedBy: userId,
|
||||
columns.UpdatedAt: updatedAt,
|
||||
}).
|
||||
Update(); err != nil {
|
||||
return gerror.Wrap(err, "更新财务申请审核状态失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
||||
"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/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTFinanceAuditRecord struct{}
|
||||
|
||||
func NewCommonTFinanceAuditRecord() *sCommonTFinanceAuditRecord {
|
||||
return &sCommonTFinanceAuditRecord{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTFinanceAuditRecord(NewCommonTFinanceAuditRecord())
|
||||
}
|
||||
|
||||
// Model 财务审核记录表ORM模型
|
||||
func (s *sCommonTFinanceAuditRecord) 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.TFinanceAuditRecord.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取财务审核记录表列表
|
||||
func (s *sCommonTFinanceAuditRecord) List(ctx context.Context, in *model.TFinanceAuditRecordListParam) (list []*model.TFinanceAuditRecordListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
auditRecordTable := dao.TFinanceAuditRecord.Table()
|
||||
sysDept := dao.SysDept.Table()
|
||||
sysUser := dao.SysUser.Table()
|
||||
mod = mod.LeftJoin(sysDept, sysDept+"."+dao.SysDept.Columns().DeptId+"="+auditRecordTable+"."+dao.TFinanceAuditRecord.Columns().AuditDeptId)
|
||||
mod = mod.LeftJoin(sysUser, sysUser+"."+dao.SysUser.Columns().UserId+"="+auditRecordTable+"."+dao.TFinanceAuditRecord.Columns().AuditorUserId)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.FieldsPrefix(auditRecordTable, model.TFinanceAuditRecordListModel{})
|
||||
mod = mod.Fields(sysDept + ".dept_name as auditorDeptName")
|
||||
mod = mod.Fields(sysUser + ".user_name as auditorUserName")
|
||||
|
||||
// 查询主键ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TFinanceAuditRecord.Columns().Id, in.Id)
|
||||
}
|
||||
if in.ApplyNo != nil {
|
||||
mod = mod.Where(dao.TFinanceAuditRecord.Columns().ApplyNo, *in.ApplyNo)
|
||||
}
|
||||
if in.AuditResult != nil {
|
||||
mod = mod.Where(dao.TFinanceAuditRecord.Columns().AuditResult, *in.AuditResult)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TFinanceAuditRecord.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TFinanceAuditRecord.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 *sCommonTFinanceAuditRecord) Export(ctx context.Context, in *model.TFinanceAuditRecordListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TFinanceAuditRecordExportModel{})
|
||||
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.TFinanceAuditRecordExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增财务审核记录表
|
||||
func (s *sCommonTFinanceAuditRecord) Edit(ctx context.Context, in *model.TFinanceAuditRecordEditParam) (err error) {
|
||||
// 验证'AuditNo'唯一
|
||||
if err = xgorm.IsUnique(ctx, &dao.TFinanceAuditRecord, g.Map{dao.TFinanceAuditRecord.Columns().AuditNo: in.AuditNo}, "审核记录编号已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
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.TFinanceAuditRecordUpdateFields{}).
|
||||
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.TFinanceAuditRecordInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增财务审核记录表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除财务审核记录表
|
||||
func (s *sCommonTFinanceAuditRecord) Delete(ctx context.Context, in *model.TFinanceAuditRecordDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除财务审核记录表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取财务审核记录表指定信息
|
||||
func (s *sCommonTFinanceAuditRecord) View(ctx context.Context, in *model.TFinanceAuditRecordViewParam) (res *model.TFinanceAuditRecordViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取财务审核记录表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateRecord 写入财务审核记录。
|
||||
func (s *sCommonTFinanceAuditRecord) CreateRecord(ctx context.Context, tx gdb.TX, in *model.TFinanceAuditRecordCreateParam) error {
|
||||
if in == nil {
|
||||
return gerror.New("财务审核记录参数不能为空")
|
||||
}
|
||||
columns := dao.TFinanceAuditRecord.Columns()
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
userId := contexts.GetUserId(ctx)
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
createdAt := gtime.Now()
|
||||
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TFinanceAuditRecord.Table()).Data(g.Map{
|
||||
columns.AuditNo: in.AuditNo,
|
||||
columns.ApplyNo: in.ApplyNo,
|
||||
columns.AuditDeptId: in.AuditDeptId,
|
||||
columns.AuditorUserId: in.AuditorUserId,
|
||||
columns.AuditResult: in.AuditResult,
|
||||
columns.AuditOpinion: in.AuditOpinion,
|
||||
columns.AuditTime: in.AuditTime,
|
||||
columns.TenantId: tenantId,
|
||||
columns.CreatedDept: deptId,
|
||||
columns.CreatedBy: userId,
|
||||
columns.CreatedAt: createdAt,
|
||||
}).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增财务审核记录失败,请稍后重试!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/websocket"
|
||||
"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"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTMessageLog struct{}
|
||||
|
||||
func NewCommonTMessageLog() *sCommonTMessageLog {
|
||||
return &sCommonTMessageLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTMessageLog(NewCommonTMessageLog())
|
||||
}
|
||||
|
||||
// Model 消息日志ORM模型
|
||||
func (s *sCommonTMessageLog) 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.TMessageLog.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取消息日志列表
|
||||
func (s *sCommonTMessageLog) List(ctx context.Context, in *model.TMessageLogListParam) (list []*model.TMessageLogListModel, totalCount int, err error) {
|
||||
mod := dao.TMessageLog.Ctx(ctx)
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TMessageLogListModel{})
|
||||
g.Log().Info(ctx, "查询用户ID:", contexts.GetUserId(ctx))
|
||||
|
||||
if in.MessageType != nil {
|
||||
mod = mod.Where(dao.TMessageLog.Columns().MessageType, in.MessageType)
|
||||
}
|
||||
|
||||
if title := strings.TrimSpace(in.MessageTitle); title != "" {
|
||||
mod = mod.WhereLike(dao.TMessageLog.Columns().MessageTitle, "%"+title+"%")
|
||||
}
|
||||
|
||||
// 查询发送时间范围
|
||||
if in.SendTimeStart != nil {
|
||||
mod = mod.WhereGTE(dao.TMessageLog.Columns().SendTime, in.SendTimeStart)
|
||||
}
|
||||
if in.SendTimeEnd != nil {
|
||||
mod = mod.WhereLTE(dao.TMessageLog.Columns().SendTime, in.SendTimeEnd.EndOfDay())
|
||||
}
|
||||
|
||||
// 兼容旧的单日发送时间查询
|
||||
if in.SendTime != nil {
|
||||
mod = mod.Where(fmt.Sprintf("DATE(%s) = ?", dao.TMessageLog.Columns().SendTime), in.SendTime.Format("Y-m-d"))
|
||||
}
|
||||
|
||||
// 查询用户ID(JSON字段,使用JSON_CONTAINS查询)
|
||||
userId := contexts.GetUserId(ctx)
|
||||
var sysUser *entity.SysUser
|
||||
dao.SysUser.Ctx(ctx).WherePri(userId).Scan(&sysUser)
|
||||
userType := sysUser.UserType
|
||||
|
||||
// 1. 查找顶级部门(ParentId = 0)
|
||||
var topDept entity.SysDept
|
||||
err = dao.SysDept.Ctx(ctx).
|
||||
Where(dao.SysDept.Columns().ParentId, 0).
|
||||
Scan(&topDept)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查顶级部门是否存在
|
||||
if topDept.DeptId > 0 && deptId != topDept.DeptId {
|
||||
mod = mod.Where(dao.TMessageLog.Columns().IsRecall, 0)
|
||||
mod = mod.Where(fmt.Sprintf("JSON_CONTAINS(%s, CAST(? AS JSON))", dao.TMessageLog.Columns().UserIds), userId)
|
||||
if userType == "sys_user" {
|
||||
mod = mod.Where(dao.TMessageLog.Columns().SendUserType, 1)
|
||||
} else if userType == "app_user" {
|
||||
mod = mod.Where(dao.TMessageLog.Columns().SendUserType, 2)
|
||||
}
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TMessageLog.Columns().MessageLogId)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取消息日志列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出消息日志
|
||||
func (s *sCommonTMessageLog) Export(ctx context.Context, in *model.TMessageLogListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TMessageLogExportModel{})
|
||||
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.TMessageLogExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增消息日志
|
||||
func (s *sCommonTMessageLog) Edit(ctx context.Context, in *model.TMessageLogEditParam) (err error) {
|
||||
var (
|
||||
pushUserIds []uint64
|
||||
pushPayload g.Map
|
||||
)
|
||||
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.MessageLogId > 0 {
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
updatedBy := contexts.GetUserId(ctx)
|
||||
in.UpdatedBy = &updatedBy
|
||||
in.UpdatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TMessageLogUpdateFields{}).
|
||||
WherePri(in.MessageLogId).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()
|
||||
in.SendTime = gtime.Now()
|
||||
|
||||
result, err := tx.Ctx(ctx).Model(dao.TMessageLog.Table()).
|
||||
Fields(model.TMessageLogInsertFields{}).
|
||||
Data(in).Insert()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "新增消息日志失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取插入的消息日志ID
|
||||
messageLogId, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "获取消息日志ID失败")
|
||||
return
|
||||
}
|
||||
pushPayload = g.Map{
|
||||
"messageLogId": messageLogId,
|
||||
"messageType": gconv.Uint(in.MessageType),
|
||||
"sendUserType": gconv.Uint(in.SendUserType),
|
||||
"messageTitle": gconv.String(in.MessageTitle),
|
||||
"messageContent": gconv.String(in.MessageContent),
|
||||
"messageLink": gconv.String(in.MessageLink),
|
||||
"sendTime": in.SendTime,
|
||||
"isRecall": gconv.Uint(in.IsRecall),
|
||||
"isRead": 0,
|
||||
}
|
||||
|
||||
optionalUserMessageColumns := s.userMessageOptionalColumns(ctx)
|
||||
// 根据 user_ids 往用户消息表插入记录
|
||||
if in.UserIds != nil && len(in.UserIds.Interfaces()) > 0 {
|
||||
userIds := in.UserIds.Interfaces()
|
||||
for _, userId := range userIds {
|
||||
userIdInt := gconv.Int64(userId)
|
||||
if userIdInt <= 0 {
|
||||
continue
|
||||
}
|
||||
userMessageData := g.Map{
|
||||
dao.TUserMessage.Columns().MessageLogId: messageLogId,
|
||||
dao.TUserMessage.Columns().UserId: userIdInt,
|
||||
dao.TUserMessage.Columns().TenantId: tenantId,
|
||||
dao.TUserMessage.Columns().CreatedDept: createdDept,
|
||||
dao.TUserMessage.Columns().CreatedBy: createdBy,
|
||||
dao.TUserMessage.Columns().CreatedAt: in.CreatedAt,
|
||||
}
|
||||
if _, ok := optionalUserMessageColumns["user_rank"]; ok {
|
||||
userMessageData["user_rank"] = 0
|
||||
}
|
||||
if _, ok := optionalUserMessageColumns["title"]; ok {
|
||||
userMessageData["title"] = messageLogStringValue(in.MessageTitle)
|
||||
}
|
||||
if _, ok := optionalUserMessageColumns["content"]; ok {
|
||||
userMessageData["content"] = messageLogStringValue(in.MessageContent)
|
||||
}
|
||||
if _, ok := optionalUserMessageColumns["link"]; ok {
|
||||
userMessageData["link"] = messageLogStringValue(in.MessageLink)
|
||||
}
|
||||
_, err = tx.Ctx(ctx).Model(dao.TUserMessage.Table()).Data(userMessageData).Insert()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "新增用户消息失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
pushUserIds = append(pushUserIds, uint64(userIdInt))
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, userId := range pushUserIds {
|
||||
g.Log().Info(ctx, "推送消息到用户 %v", userId)
|
||||
websocket.SendToUser(userId, &websocket.WResponse{
|
||||
Event: "message:new",
|
||||
Data: pushPayload,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonTMessageLog) userMessageOptionalColumns(ctx context.Context) map[string]struct{} {
|
||||
result, err := g.DB().GetAll(ctx, "SHOW COLUMNS FROM "+dao.TUserMessage.Table())
|
||||
if err != nil {
|
||||
return map[string]struct{}{}
|
||||
}
|
||||
columns := make(map[string]struct{}, len(result))
|
||||
for _, row := range result {
|
||||
field := row["Field"].String()
|
||||
if field != "" {
|
||||
columns[field] = struct{}{}
|
||||
}
|
||||
}
|
||||
return columns
|
||||
}
|
||||
|
||||
func messageLogStringValue(value *string) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
return *value
|
||||
}
|
||||
|
||||
// Delete 删除消息日志
|
||||
func (s *sCommonTMessageLog) Delete(ctx context.Context, in *model.TMessageLogDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.MessageLogId).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除消息日志失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取消息日志指定信息,并标记已读
|
||||
func (s *sCommonTMessageLog) View(ctx context.Context, in *model.TMessageLogViewParam) (res *model.TMessageLogViewModel, err error) {
|
||||
if err = dao.TMessageLog.Ctx(ctx).WherePri(in.MessageLogId).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取消息日志信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
// 标记用户消息为已读
|
||||
userId := contexts.GetUserId(ctx)
|
||||
now := gtime.Now()
|
||||
_, err = dao.TUserMessage.Ctx(ctx).Where(g.Map{
|
||||
dao.TUserMessage.Columns().MessageLogId: in.MessageLogId,
|
||||
dao.TUserMessage.Columns().UserId: userId,
|
||||
dao.TUserMessage.Columns().IsRead: 0,
|
||||
}).Data(g.Map{
|
||||
dao.TUserMessage.Columns().IsRead: 1,
|
||||
dao.TUserMessage.Columns().UpdatedBy: userId,
|
||||
dao.TUserMessage.Columns().UpdatedAt: now,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
// 已读标记失败不影响主业务,仅记录日志
|
||||
g.Log().Warning(ctx, "标记消息已读失败: %v", err)
|
||||
err = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/websocket"
|
||||
"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"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTNotifyLog struct{}
|
||||
|
||||
func NewCommonTNotifyLog() *sCommonTNotifyLog {
|
||||
return &sCommonTNotifyLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTNotifyLog(NewCommonTNotifyLog())
|
||||
}
|
||||
|
||||
// Model 通知日志ORM模型
|
||||
func (s *sCommonTNotifyLog) 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.TNotifyLog.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取通知日志列表
|
||||
func (s *sCommonTNotifyLog) List(ctx context.Context, in *model.TNotifyLogListParam) (list []*model.TNotifyLogListModel, totalCount int, err error) {
|
||||
mod := dao.TNotifyLog.Ctx(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TNotifyLogListModel{})
|
||||
|
||||
if title := strings.TrimSpace(in.NotifyTitle); title != "" {
|
||||
mod = mod.WhereLike(dao.TNotifyLog.Columns().NotifyTitle, "%"+title+"%")
|
||||
}
|
||||
|
||||
// 查询发送用户类型
|
||||
if in.SendUserType != nil && *in.SendUserType > 0 {
|
||||
mod = mod.Where(dao.TNotifyLog.Columns().SendUserType, *in.SendUserType)
|
||||
}
|
||||
|
||||
// 查询发送时间范围
|
||||
if in.SendTimeStart != nil {
|
||||
mod = mod.WhereGTE(dao.TNotifyLog.Columns().SendTime, in.SendTimeStart)
|
||||
}
|
||||
if in.SendTimeEnd != nil {
|
||||
mod = mod.WhereLTE(dao.TNotifyLog.Columns().SendTime, in.SendTimeEnd.EndOfDay())
|
||||
}
|
||||
|
||||
// 兼容旧的单日发送时间查询
|
||||
if in.SendTime != nil {
|
||||
mod = mod.Where(fmt.Sprintf("DATE(%s) = ?", dao.TNotifyLog.Columns().SendTime), in.SendTime.Format("Y-m-d"))
|
||||
}
|
||||
|
||||
userId := contexts.GetUserId(ctx)
|
||||
var sysUser *entity.SysUser
|
||||
dao.SysUser.Ctx(ctx).WherePri(userId).Scan(&sysUser)
|
||||
userType := sysUser.UserType
|
||||
// 1. 查找顶级部门(ParentId = 0)
|
||||
var topDept entity.SysDept
|
||||
err = dao.SysDept.Ctx(ctx).
|
||||
Where(dao.SysDept.Columns().ParentId, 0).
|
||||
Scan(&topDept)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前用户部门ID
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
// 只有非顶级用户才根据用户类型过滤
|
||||
if deptId != topDept.DeptId {
|
||||
if userType == "sys_user" {
|
||||
mod = mod.Where(dao.TNotifyLog.Columns().SendUserType, 1)
|
||||
} else if userType == "app_user" {
|
||||
mod = mod.Where(dao.TNotifyLog.Columns().SendUserType, 2)
|
||||
}
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TNotifyLog.Columns().NotifyId)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取通知日志列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出通知日志
|
||||
func (s *sCommonTNotifyLog) Export(ctx context.Context, in *model.TNotifyLogListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TNotifyLogExportModel{})
|
||||
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.TNotifyLogExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增通知日志
|
||||
func (s *sCommonTNotifyLog) Edit(ctx context.Context, in *model.TNotifyLogEditParam) (err error) {
|
||||
var (
|
||||
pushPayload g.Map
|
||||
pushUserIds []uint64
|
||||
)
|
||||
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.NotifyId > 0 {
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
in.TenantId = &tenantId
|
||||
updatedBy := contexts.GetUserId(ctx)
|
||||
in.UpdatedBy = &updatedBy
|
||||
in.UpdatedAt = gtime.Now()
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(model.TNotifyLogUpdateFields{}).
|
||||
WherePri(in.NotifyId).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()
|
||||
in.SendTime = gtime.Now()
|
||||
|
||||
result, err := s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(model.TNotifyLogInsertFields{}).
|
||||
Data(in).Insert()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "新增通知日志失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
notifyId, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "获取通知ID失败")
|
||||
return
|
||||
}
|
||||
|
||||
pushPayload = g.Map{
|
||||
"notifyId": notifyId,
|
||||
"notifyTitle": gconv.String(in.NotifyTitle),
|
||||
"notifyContent": gconv.String(in.NotifyContent),
|
||||
"sendUserType": gconv.Int(in.SendUserType),
|
||||
"sendTime": in.SendTime,
|
||||
}
|
||||
|
||||
var userType string
|
||||
if in.SendUserType != nil {
|
||||
switch *in.SendUserType {
|
||||
case 1:
|
||||
userType = "sys_user"
|
||||
case 2:
|
||||
userType = "app_user"
|
||||
}
|
||||
}
|
||||
if userType != "" {
|
||||
var users []*entity.SysUser
|
||||
if err = tx.Ctx(ctx).Model(dao.SysUser.Table()).
|
||||
Fields(dao.SysUser.Columns().UserId).
|
||||
Where(dao.SysUser.Columns().TenantId, tenantId).
|
||||
Where(dao.SysUser.Columns().UserType, userType).
|
||||
Where(dao.SysUser.Columns().Status, "0").
|
||||
Scan(&users); err != nil {
|
||||
err = gerror.Wrap(err, "获取通知推送用户失败")
|
||||
return
|
||||
}
|
||||
for _, user := range users {
|
||||
if user.UserId > 0 {
|
||||
pushUserIds = append(pushUserIds, uint64(user.UserId))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, userId := range pushUserIds {
|
||||
g.Log().Info(ctx, "推送通知到用户 %v", userId)
|
||||
websocket.SendToUser(userId, &websocket.WResponse{
|
||||
Event: "notify:new",
|
||||
Data: pushPayload,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除通知日志
|
||||
func (s *sCommonTNotifyLog) Delete(ctx context.Context, in *model.TNotifyLogDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.NotifyId).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除通知日志失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取通知日志指定信息
|
||||
func (s *sCommonTNotifyLog) View(ctx context.Context, in *model.TNotifyLogViewParam) (res *model.TNotifyLogViewModel, err error) {
|
||||
if err = dao.TNotifyLog.Ctx(ctx).WherePri(in.NotifyId).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取通知日志信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,695 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||
// @AutoGenerate Version
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
||||
"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/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
"strconv"
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonProfitAccountMonthBill struct{}
|
||||
|
||||
func NewCommonProfitAccountMonthBill() *sCommonProfitAccountMonthBill {
|
||||
return &sCommonProfitAccountMonthBill{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonProfitAccountMonthBill(NewCommonProfitAccountMonthBill())
|
||||
}
|
||||
|
||||
// Model 盈利账户月账单表ORM模型
|
||||
func (s *sCommonProfitAccountMonthBill) 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.TProfitAccountMonthBill.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取盈利账户月账单表列表
|
||||
func (s *sCommonProfitAccountMonthBill) List(ctx context.Context, in *model.TProfitAccountMonthBillListParam) (list []*model.TProfitAccountMonthBillListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(model.TProfitAccountMonthBillListModel{})
|
||||
|
||||
// 查询账单ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TProfitAccountMonthBill.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询账单状态:1正常,2已调整,3已作废
|
||||
if in.BillStatus > 0 {
|
||||
mod = mod.Where(dao.TProfitAccountMonthBill.Columns().BillStatus, in.BillStatus)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TProfitAccountMonthBill.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TProfitAccountMonthBill.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 *sCommonProfitAccountMonthBill) Export(ctx context.Context, in *model.TProfitAccountMonthBillListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TProfitAccountMonthBillExportModel{})
|
||||
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.TProfitAccountMonthBillExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增盈利账户月账单表
|
||||
func (s *sCommonProfitAccountMonthBill) Edit(ctx context.Context, in *model.TProfitAccountMonthBillEditParam) (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.TProfitAccountMonthBillUpdateFields{}).
|
||||
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.TProfitAccountMonthBillInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增盈利账户月账单表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除盈利账户月账单表
|
||||
func (s *sCommonProfitAccountMonthBill) Delete(ctx context.Context, in *model.TProfitAccountMonthBillDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除盈利账户月账单表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取盈利账户月账单表指定信息
|
||||
func (s *sCommonProfitAccountMonthBill) View(ctx context.Context, in *model.TProfitAccountMonthBillViewParam) (res *model.TProfitAccountMonthBillViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取盈利账户月账单表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SettleProfitAccountMonthBill 按可提现变动明细统计并写入盈利账户月账单。
|
||||
func (s *sCommonProfitAccountMonthBill) SettleProfitAccountMonthBill(ctx context.Context, in *model.TProfitAccountMonthBillSettleParam) (res *model.TProfitAccountMonthBillSettleResult, err error) {
|
||||
if in == nil {
|
||||
in = &model.TProfitAccountMonthBillSettleParam{}
|
||||
}
|
||||
if err = in.Filter(ctx); err != nil {
|
||||
return nil, gerror.Wrap(err, "账期格式错误,请使用YYYY-MM")
|
||||
}
|
||||
|
||||
_, err = time.Parse("2006-01", in.AccountPeriod)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "账期格式错误,请使用YYYY-MM")
|
||||
}
|
||||
now := gtime.Now()
|
||||
userId := contexts.GetUserId(ctx)
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
changeColumns := dao.TFinWithdrawableChange.Columns()
|
||||
billColumns := dao.TProfitAccountMonthBill.Columns()
|
||||
|
||||
type changeRow struct {
|
||||
Id int64 `orm:"id"`
|
||||
DeptId int64 `orm:"dept_id"`
|
||||
TenantId string `orm:"tenant_id"`
|
||||
CreatedDept int64 `orm:"created_dept"`
|
||||
ChangeType int `orm:"change_type"`
|
||||
IncomeExpenseType int `orm:"income_expense_type"`
|
||||
ChangeAmount float64 `orm:"change_amount"`
|
||||
BeforeBalance float64 `orm:"before_balance"`
|
||||
CreatedAt *gtime.Time `orm:"created_at"`
|
||||
}
|
||||
|
||||
var rows []*changeRow
|
||||
mod := handler.Model(dao.TFinWithdrawableChange.Ctx(ctx), &handler.Option{
|
||||
FilterTenant: false,
|
||||
FilterAuth: false,
|
||||
}).
|
||||
Fields(
|
||||
changeColumns.Id,
|
||||
changeColumns.DeptId,
|
||||
changeColumns.TenantId,
|
||||
changeColumns.CreatedDept,
|
||||
changeColumns.ChangeType,
|
||||
changeColumns.IncomeExpenseType,
|
||||
changeColumns.ChangeAmount,
|
||||
changeColumns.BeforeBalance,
|
||||
changeColumns.CreatedAt,
|
||||
).
|
||||
Where(changeColumns.AccountPeriod, in.AccountPeriod)
|
||||
if tenantId != "" {
|
||||
mod = mod.Where(changeColumns.TenantId, tenantId)
|
||||
}
|
||||
if err = mod.OrderAsc(changeColumns.TenantId).OrderAsc(changeColumns.DeptId).OrderAsc(changeColumns.CreatedAt).OrderAsc(changeColumns.Id).Scan(&rows); err != nil {
|
||||
return nil, gerror.Wrap(err, "查询可提现变动明细失败,请稍后重试!")
|
||||
}
|
||||
|
||||
type billGroup struct {
|
||||
DeptId int64
|
||||
TenantId string
|
||||
CreatedDept int64
|
||||
AccountPeriod string
|
||||
OpeningBalance float64
|
||||
CommissionIncome float64
|
||||
BalanceTransferInIncome float64
|
||||
WithdrawalExpense float64
|
||||
WithdrawableTransferOutExpense float64
|
||||
ProfitCreditRepayExpense float64
|
||||
TotalIncome float64
|
||||
TotalExpense float64
|
||||
IncomeCount int
|
||||
ExpenseCount int
|
||||
DetailCount int
|
||||
}
|
||||
|
||||
groups := make(map[string]*billGroup)
|
||||
keys := make([]string, 0, 16)
|
||||
for _, row := range rows {
|
||||
if row == nil || row.DeptId <= 0 {
|
||||
continue
|
||||
}
|
||||
switch row.ChangeType {
|
||||
case 0, 1, 2, 3, 4:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
key := fmt.Sprintf("%s:%d", row.TenantId, row.DeptId)
|
||||
group := groups[key]
|
||||
if group == nil {
|
||||
group = &billGroup{
|
||||
DeptId: row.DeptId,
|
||||
TenantId: row.TenantId,
|
||||
CreatedDept: row.CreatedDept,
|
||||
AccountPeriod: in.AccountPeriod,
|
||||
OpeningBalance: row.BeforeBalance,
|
||||
}
|
||||
groups[key] = group
|
||||
keys = append(keys, key)
|
||||
}
|
||||
group.DetailCount++
|
||||
switch row.ChangeType {
|
||||
case 0:
|
||||
group.CommissionIncome += row.ChangeAmount
|
||||
case 1:
|
||||
group.WithdrawalExpense += row.ChangeAmount
|
||||
case 2:
|
||||
group.WithdrawableTransferOutExpense += row.ChangeAmount
|
||||
case 3:
|
||||
group.BalanceTransferInIncome += row.ChangeAmount
|
||||
case 4:
|
||||
group.ProfitCreditRepayExpense += row.ChangeAmount
|
||||
}
|
||||
if row.IncomeExpenseType == 0 {
|
||||
group.TotalIncome += row.ChangeAmount
|
||||
group.IncomeCount++
|
||||
} else if row.IncomeExpenseType == 1 {
|
||||
group.TotalExpense += row.ChangeAmount
|
||||
group.ExpenseCount++
|
||||
}
|
||||
}
|
||||
|
||||
res = &model.TProfitAccountMonthBillSettleResult{
|
||||
AccountPeriod: in.AccountPeriod,
|
||||
}
|
||||
if len(groups) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
for _, key := range keys {
|
||||
group := groups[key]
|
||||
if group == nil {
|
||||
continue
|
||||
}
|
||||
totalIncome := group.TotalIncome
|
||||
totalExpense := group.TotalExpense
|
||||
closingBalance := group.OpeningBalance + totalIncome - totalExpense
|
||||
group.TotalIncome = totalIncome
|
||||
group.TotalExpense = totalExpense
|
||||
|
||||
lookup := tx.Ctx(ctx).Model(dao.TProfitAccountMonthBill.Table()).
|
||||
Fields(billColumns.Id).
|
||||
Where(billColumns.DeptId, group.DeptId).
|
||||
Where(billColumns.AccountPeriod, group.AccountPeriod)
|
||||
if group.TenantId != "" {
|
||||
lookup = lookup.Where(billColumns.TenantId, group.TenantId)
|
||||
}
|
||||
var existing struct {
|
||||
Id int64 `orm:"id"`
|
||||
}
|
||||
if err := lookup.LockUpdate().Scan(&existing); err != nil && err != sql.ErrNoRows {
|
||||
return gerror.Wrap(err, "查询盈利账户月账单失败,请稍后重试!")
|
||||
}
|
||||
|
||||
tenantValue := group.TenantId
|
||||
if tenantValue == "" {
|
||||
tenantValue = tenantId
|
||||
}
|
||||
if tenantValue == "" {
|
||||
return gerror.New("租户ID不能为空")
|
||||
}
|
||||
createdDeptValue := group.CreatedDept
|
||||
if createdDeptValue == 0 {
|
||||
createdDeptValue = contexts.GetDeptId(ctx)
|
||||
}
|
||||
if createdDeptValue == 0 {
|
||||
return gerror.New("创建部门不能为空")
|
||||
}
|
||||
|
||||
data := g.Map{
|
||||
billColumns.DeptId: group.DeptId,
|
||||
billColumns.AccountPeriod: group.AccountPeriod,
|
||||
billColumns.OpeningBalance: group.OpeningBalance,
|
||||
billColumns.CommissionIncome: group.CommissionIncome,
|
||||
billColumns.BalanceTransferInIncome: group.BalanceTransferInIncome,
|
||||
billColumns.WithdrawalExpense: group.WithdrawalExpense,
|
||||
billColumns.WithdrawableTransferOutExpense: group.WithdrawableTransferOutExpense,
|
||||
billColumns.ProfitCreditRepayExpense: group.ProfitCreditRepayExpense,
|
||||
billColumns.TotalIncome: totalIncome,
|
||||
billColumns.TotalExpense: totalExpense,
|
||||
billColumns.ClosingBalance: closingBalance,
|
||||
billColumns.IncomeCount: group.IncomeCount,
|
||||
billColumns.ExpenseCount: group.ExpenseCount,
|
||||
billColumns.BillStatus: 1,
|
||||
billColumns.SettleTime: now,
|
||||
billColumns.SettleBy: userId,
|
||||
billColumns.Remark: "根据可提现变动明细统计生成",
|
||||
billColumns.TenantId: tenantValue,
|
||||
billColumns.UpdatedBy: userId,
|
||||
billColumns.UpdatedAt: now,
|
||||
}
|
||||
if existing.Id > 0 {
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TProfitAccountMonthBill.Table()).
|
||||
WherePri(existing.Id).
|
||||
Data(data).
|
||||
Update(); err != nil {
|
||||
return gerror.Wrap(err, "更新盈利账户月账单失败,请稍后重试!")
|
||||
}
|
||||
res.UpdateCount++
|
||||
} else {
|
||||
data[billColumns.CreatedDept] = createdDeptValue
|
||||
data[billColumns.CreatedBy] = userId
|
||||
data[billColumns.CreatedAt] = now
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TProfitAccountMonthBill.Table()).
|
||||
Data(data).
|
||||
Insert(); err != nil {
|
||||
return gerror.Wrap(err, "新增盈利账户月账单失败,请稍后重试!")
|
||||
}
|
||||
res.InsertCount++
|
||||
}
|
||||
res.DeptCount++
|
||||
res.DetailCount += group.DetailCount
|
||||
res.TotalIncome += totalIncome
|
||||
res.TotalExpense += totalExpense
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 获取月账单盈利账户汇总信息
|
||||
func (s *sCommonProfitAccountMonthBill) GetProfitBillSummary(ctx context.Context, month string) (res *model.TProfitBillModel, err error) {
|
||||
// 计算上月账期(基于当前时间)
|
||||
// now := time.Now()
|
||||
// lastMonthTime := now.AddDate(0, -1, 0)
|
||||
// lastMonthPeriod := lastMonthTime.Format("2006-01")
|
||||
|
||||
var bill *model.TProfitAccountMonthBillViewModel
|
||||
if err = dao.TProfitAccountMonthBill.Ctx(ctx).
|
||||
Where(dao.TProfitAccountMonthBill.Columns().AccountPeriod, month).
|
||||
Where(dao.TProfitAccountMonthBill.Columns().DeptId, contexts.GetDeptId(ctx)).
|
||||
Scan(&bill); err != nil {
|
||||
err = gerror.Wrap(err, "查询上月账单失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if bill == nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = &model.TProfitBillModel{}
|
||||
res.TotalIncome = bill.TotalIncome
|
||||
res.TotalExpense = bill.TotalExpense
|
||||
return
|
||||
}
|
||||
|
||||
// GetCoreFinancialData 获取核心财务指标
|
||||
func (s *sCommonProfitAccountMonthBill) GetCoreFinancialData(ctx context.Context, in *model.TCommissionOverviewParam) (res *model.TCoreFinancialDataModel, err error) {
|
||||
res = &model.TCoreFinancialDataModel{}
|
||||
deptId := contexts.GetDeptId(ctx)
|
||||
|
||||
// 解析账期参数
|
||||
var startPeriod, endPeriod string
|
||||
if in.Type == 1 { // 年
|
||||
startPeriod = in.Year + "-01"
|
||||
endPeriod = in.Year + "-12"
|
||||
} else if in.Type == 2 { // 月
|
||||
startPeriod = in.Month
|
||||
endPeriod = in.Month
|
||||
}
|
||||
|
||||
// 1. 获取总收入:统计 t_profit_account_month_bill 的 total_income
|
||||
type totalIncomeRow struct {
|
||||
TotalIncome float64 `orm:"total_income"`
|
||||
}
|
||||
var totalIncomeResult *totalIncomeRow
|
||||
if err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Where(dao.TProfitAccountMonthBill.Columns().DeptId, deptId).
|
||||
WhereGTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, endPeriod).
|
||||
Fields("SUM(" + dao.TProfitAccountMonthBill.Columns().TotalIncome + ") as total_income").
|
||||
Scan(&totalIncomeResult); err != nil {
|
||||
err = gerror.Wrap(err, "查询总收入失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if totalIncomeResult != nil {
|
||||
res.TotalIncome = totalIncomeResult.TotalIncome
|
||||
}
|
||||
|
||||
// 2. 获取顶级利润:查询 t_fin_commission_detail 中顶级部门的 commission_income
|
||||
// 顶级部门ID通常为1或当前部门的顶级父部门
|
||||
|
||||
type topProfitRow struct {
|
||||
CommissionIncome float64 `orm:"commission_income"`
|
||||
}
|
||||
var topProfitResult *topProfitRow
|
||||
if err = handler.Model(dao.TFinCommissionDetail.Ctx(ctx)).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptId, deptId).
|
||||
WhereGTE(dao.TFinCommissionDetail.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TFinCommissionDetail.Columns().AccountPeriod, endPeriod).
|
||||
Fields("SUM(" + dao.TFinCommissionDetail.Columns().CommissionAmount + ") as commission_income").
|
||||
Scan(&topProfitResult); err != nil {
|
||||
err = gerror.Wrap(err, "查询顶级利润失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if topProfitResult != nil {
|
||||
res.TopLevelProfit = topProfitResult.CommissionIncome
|
||||
}
|
||||
|
||||
// 3. 获取待审核提现:从 t_profit_account_month_bill 查询 withdrawable_transfer_out_expense
|
||||
type pendingWithdrawRow struct {
|
||||
WithdrawableTransferOutExpense float64 `orm:"withdrawable_transfer_out_expense"`
|
||||
}
|
||||
var pendingWithdrawResult *pendingWithdrawRow
|
||||
if err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Where(dao.TProfitAccountMonthBill.Columns().DeptId, deptId).
|
||||
WhereGTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, endPeriod).
|
||||
Fields("SUM(" + dao.TProfitAccountMonthBill.Columns().WithdrawableTransferOutExpense + ") as withdrawable_transfer_out_expense").
|
||||
Scan(&pendingWithdrawResult); err != nil {
|
||||
err = gerror.Wrap(err, "查询待审核提现失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if pendingWithdrawResult != nil {
|
||||
res.PendingWithdrawAmount = pendingWithdrawResult.WithdrawableTransferOutExpense
|
||||
}
|
||||
|
||||
// 4. 获取已提现金额:统计 t_profit_account_month_bill 的 balance_transfer_in_income
|
||||
type withdrawedRow struct {
|
||||
WithdrawalExpense float64 `orm:"withdrawal_expense"`
|
||||
}
|
||||
var withdrawedResult *withdrawedRow
|
||||
if err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Where(dao.TProfitAccountMonthBill.Columns().DeptId, deptId).
|
||||
WhereGTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, startPeriod).
|
||||
WhereLTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, endPeriod).
|
||||
Fields("SUM(" + dao.TProfitAccountMonthBill.Columns().WithdrawalExpense + ") as withdrawal_expense").
|
||||
Scan(&withdrawedResult); err != nil {
|
||||
err = gerror.Wrap(err, "查询已提现金额失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
if withdrawedResult != nil {
|
||||
res.WithdrawedAmount = withdrawedResult.WithdrawalExpense
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetFinancialTrend 获取财务趋势折线图
|
||||
// 指标类型:1总收入,2顶级利润,3待审核提现,4已提现金额
|
||||
// 月份视图:按天查询 t_fin_withdrawable_change
|
||||
// 年份视图:按月查询 t_profit_account_month_bill
|
||||
func (s *sCommonProfitAccountMonthBill) GetFinancialTrend(ctx context.Context, in *model.TFinancialTrendParam) (res *model.TFinancialTrendModel, err error) {
|
||||
res = &model.TFinancialTrendModel{}
|
||||
|
||||
// 默认查询所有指标
|
||||
indicators := in.Indicators
|
||||
if len(indicators) == 0 {
|
||||
indicators = []int{1, 2, 3, 4}
|
||||
}
|
||||
|
||||
// 指标名称映射
|
||||
indicatorNames := map[int]string{
|
||||
1: "总收入",
|
||||
2: "顶级利润",
|
||||
3: "待审核提现",
|
||||
4: "已提现金额",
|
||||
}
|
||||
|
||||
if in.Type == 1 {
|
||||
// 年视图:按月查询 t_profit_account_month_bill
|
||||
year := in.Year
|
||||
months := []string{"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"}
|
||||
res.Labels = make([]string, 0, 12)
|
||||
for _, m := range months {
|
||||
res.Labels = append(res.Labels, year+"-"+m)
|
||||
}
|
||||
|
||||
type monthBillRow struct {
|
||||
AccountPeriod string `orm:"account_period"`
|
||||
TotalIncome float64 `orm:"total_income"`
|
||||
CommissionIncome float64 `orm:"commission_income"`
|
||||
BalanceTransferInIncome float64 `orm:"balance_transfer_in_income"`
|
||||
WithdrawalExpense float64 `orm:"withdrawal_expense"`
|
||||
}
|
||||
var bills []*monthBillRow
|
||||
if err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Where(dao.TProfitAccountMonthBill.Columns().DeptId, contexts.GetDeptId(ctx)).
|
||||
WhereGTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, year+"-01").
|
||||
WhereLTE(dao.TProfitAccountMonthBill.Columns().AccountPeriod, year+"-12").
|
||||
Fields(
|
||||
dao.TProfitAccountMonthBill.Columns().AccountPeriod,
|
||||
dao.TProfitAccountMonthBill.Columns().TotalIncome,
|
||||
dao.TProfitAccountMonthBill.Columns().CommissionIncome,
|
||||
dao.TProfitAccountMonthBill.Columns().BalanceTransferInIncome,
|
||||
dao.TProfitAccountMonthBill.Columns().WithdrawalExpense,
|
||||
).
|
||||
Scan(&bills); err != nil {
|
||||
return nil, gerror.Wrap(err, "查询财务趋势数据失败,请稍后重试!")
|
||||
}
|
||||
|
||||
// 构建月份映射
|
||||
billMap := make(map[string]*monthBillRow)
|
||||
for _, bill := range bills {
|
||||
billMap[bill.AccountPeriod] = bill
|
||||
}
|
||||
|
||||
// 构建每个指标的数据数组
|
||||
res.Series = make([]model.TFinancialTrendSeries, 0, len(indicators))
|
||||
for _, ind := range indicators {
|
||||
series := model.TFinancialTrendSeries{
|
||||
Name: indicatorNames[ind],
|
||||
Type: ind,
|
||||
Values: make([]float64, 0, 12),
|
||||
}
|
||||
for _, period := range res.Labels {
|
||||
var val float64
|
||||
if bill, ok := billMap[period]; ok {
|
||||
switch ind {
|
||||
case 1:
|
||||
val = bill.TotalIncome
|
||||
case 2:
|
||||
val = bill.CommissionIncome
|
||||
case 3:
|
||||
val = bill.BalanceTransferInIncome
|
||||
case 4:
|
||||
val = bill.WithdrawalExpense
|
||||
}
|
||||
}
|
||||
series.Values = append(series.Values, val)
|
||||
}
|
||||
res.Series = append(res.Series, series)
|
||||
}
|
||||
|
||||
} else if in.Type == 2 {
|
||||
// 月视图:按天查询 t_fin_withdrawable_change
|
||||
month := in.Month
|
||||
t := gtime.NewFromStr(month + "-01")
|
||||
daysInMonth := t.EndOfMonth().Day()
|
||||
|
||||
res.Labels = make([]string, 0, daysInMonth)
|
||||
for day := 1; day <= daysInMonth; day++ {
|
||||
res.Labels = append(res.Labels, month+"-"+strconv.Itoa(day))
|
||||
}
|
||||
|
||||
// 解析月份获取开始和结束日期
|
||||
startDate := month + "-01"
|
||||
endDate := month + "-" + strconv.Itoa(daysInMonth)
|
||||
|
||||
type dailyRow struct {
|
||||
Date string `orm:"date"`
|
||||
IncomeAmt float64 `orm:"income_amt"`
|
||||
Expense1Amt float64 `orm:"expense1_amt"`
|
||||
Expense3Amt float64 `orm:"expense3_amt"`
|
||||
}
|
||||
var dailyResults []*dailyRow
|
||||
if err = handler.Model(dao.TFinWithdrawableChange.Ctx(ctx)).
|
||||
Where(dao.TFinWithdrawableChange.Columns().DeptId, contexts.GetDeptId(ctx)).
|
||||
WhereGTE(dao.TFinWithdrawableChange.Columns().CreatedAt, startDate).
|
||||
WhereLTE(dao.TFinWithdrawableChange.Columns().CreatedAt, endDate).
|
||||
Fields(`
|
||||
DATE_FORMAT(` + dao.TFinWithdrawableChange.Columns().CreatedAt + `, '%Y-%m-%d') as date,
|
||||
SUM(CASE WHEN ` + dao.TFinWithdrawableChange.Columns().IncomeExpenseType + `=0 THEN ` + dao.TFinWithdrawableChange.Columns().ChangeAmount + ` ELSE 0 END) as income_amt,
|
||||
SUM(CASE WHEN ` + dao.TFinWithdrawableChange.Columns().IncomeExpenseType + `=1 THEN ` + dao.TFinWithdrawableChange.Columns().ChangeAmount + ` ELSE 0 END) as expense1_amt,
|
||||
SUM(CASE WHEN ` + dao.TFinWithdrawableChange.Columns().IncomeExpenseType + `=3 THEN ` + dao.TFinWithdrawableChange.Columns().ChangeAmount + ` ELSE 0 END) as expense3_amt
|
||||
`).
|
||||
Group("DATE_FORMAT(" + dao.TFinWithdrawableChange.Columns().CreatedAt + ", '%Y-%m-%d')").
|
||||
Order("date ASC").
|
||||
Scan(&dailyResults); err != nil {
|
||||
return nil, gerror.Wrap(err, "查询日趋势数据失败,请稍后重试!")
|
||||
}
|
||||
|
||||
// 构建日期映射
|
||||
dailyMap := make(map[string]*dailyRow)
|
||||
for _, row := range dailyResults {
|
||||
dailyMap[row.Date] = row
|
||||
}
|
||||
|
||||
// 查询顶级利润(从分润表)
|
||||
type commissionRow struct {
|
||||
CommissionAmount float64 `orm:"commission_amount"`
|
||||
}
|
||||
var commissionResult *commissionRow
|
||||
if err = handler.Model(dao.TFinCommissionDetail.Ctx(ctx)).
|
||||
Where(dao.TFinCommissionDetail.Columns().BenefitDeptId, contexts.GetDeptId(ctx)).
|
||||
WhereGTE(dao.TFinCommissionDetail.Columns().AccountPeriod, month).
|
||||
WhereLTE(dao.TFinCommissionDetail.Columns().AccountPeriod, month).
|
||||
Fields("SUM(" + dao.TFinCommissionDetail.Columns().CommissionAmount + ") as commission_amount").
|
||||
Scan(&commissionResult); err != nil {
|
||||
return nil, gerror.Wrap(err, "查询顶级利润失败,请稍后重试!")
|
||||
}
|
||||
|
||||
topProfit := 0.0
|
||||
if commissionResult != nil {
|
||||
topProfit = commissionResult.CommissionAmount
|
||||
}
|
||||
|
||||
// 构建每个指标的数据数组
|
||||
res.Series = make([]model.TFinancialTrendSeries, 0, len(indicators))
|
||||
for _, ind := range indicators {
|
||||
series := model.TFinancialTrendSeries{
|
||||
Name: indicatorNames[ind],
|
||||
Type: ind,
|
||||
Values: make([]float64, 0, daysInMonth),
|
||||
}
|
||||
for _, date := range res.Labels {
|
||||
var val float64
|
||||
switch ind {
|
||||
case 1: // 总收入
|
||||
if row, ok := dailyMap[date]; ok {
|
||||
val = row.IncomeAmt
|
||||
}
|
||||
case 2: // 顶级利润
|
||||
val = topProfit
|
||||
case 3: // 待审核提现
|
||||
if row, ok := dailyMap[date]; ok {
|
||||
val = row.Expense3Amt
|
||||
}
|
||||
case 4: // 已提现金额
|
||||
if row, ok := dailyMap[date]; ok {
|
||||
val = row.Expense1Amt
|
||||
}
|
||||
}
|
||||
series.Values = append(series.Values, val)
|
||||
}
|
||||
res.Series = append(res.Series, series)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/websocket"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
||||
"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/do"
|
||||
"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"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTWoCommunication struct{}
|
||||
|
||||
func NewCommonTWoCommunication() *sCommonTWoCommunication {
|
||||
return &sCommonTWoCommunication{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTWoCommunication(NewCommonTWoCommunication())
|
||||
}
|
||||
|
||||
func (s *sCommonTWoCommunication) 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.TWoCommunication.Ctx(ctx), option...)
|
||||
}
|
||||
func (s *sCommonTWoCommunication) List(ctx context.Context, in *model.TWoCommunicationListParam) (list []*model.TWoCommunicationListModel, totalCount int, err error) {
|
||||
if in == nil || strings.TrimSpace(in.WoNo) == "" {
|
||||
return nil, 0, gerror.New("请指定工单编号")
|
||||
}
|
||||
if _, err = getWorkOrder(ctx, in.WoNo); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
mod := s.Model(ctx, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: false,
|
||||
})
|
||||
mod = mod.Fields(model.TWoCommunicationListModel{})
|
||||
|
||||
if in.WoNo != "" {
|
||||
mod = mod.Where(dao.TWoCommunication.Columns().WoNo, in.WoNo)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
mod = mod.WhereBetween(dao.TWoCommunication.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
mod = mod.OrderDesc(dao.TWoCommunication.Columns().Id)
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "get work order communication list failed")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonTWoCommunication) Export(ctx context.Context, in *model.TWoCommunicationListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tags, err := convert.GetEntityDescTags(model.TWoCommunicationExportModel{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
fileName = "export-work-order-communication-" + gctx.CtxId(ctx)
|
||||
sheetName = fmt.Sprintf("total %v rows, %v pages, page %v, current %v rows", totalCount, (totalCount+in.PageSize-1)/in.PageSize, in.Page, len(list))
|
||||
exports []model.TWoCommunicationExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonTWoCommunication) Edit(ctx context.Context, in *model.TWoCommunicationEditParam) (err error) {
|
||||
if err = xgorm.IsUnique(ctx, &dao.TWoCommunication, g.Map{dao.TWoCommunication.Columns().CommNo: in.CommNo}, "communication number exists", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
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.TWoCommunicationUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "update work order communication failed")
|
||||
}
|
||||
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.TWoCommunicationInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "create work order communication failed")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// CreateInitialMessage 创建工单时同步生成首条沟通消息。
|
||||
func (s *sCommonTWoCommunication) CreateInitialMessage(ctx context.Context, tx gdb.TX, in *model.TWoCommunicationInitialMessageParam) error {
|
||||
if in == nil {
|
||||
return gerror.New("invalid request")
|
||||
}
|
||||
if in.WoNo == "" {
|
||||
return gerror.New("work order number cannot be empty")
|
||||
}
|
||||
if in.Content == "" {
|
||||
return nil
|
||||
}
|
||||
now := in.CreatedAt
|
||||
if now == nil {
|
||||
now = gtime.Now()
|
||||
}
|
||||
columns := dao.TWoCommunication.Columns()
|
||||
data := g.Map{
|
||||
columns.WoNo: in.WoNo,
|
||||
columns.CommType: in.CommType,
|
||||
columns.SenderType: in.SenderType,
|
||||
columns.SenderDeptId: in.SenderDeptId,
|
||||
columns.SenderUserId: in.SenderUserId,
|
||||
columns.SenderName: in.SenderName,
|
||||
columns.Content: in.Content,
|
||||
columns.CommTime: now,
|
||||
columns.TenantId: in.TenantId,
|
||||
columns.CreatedDept: in.CreatedDept,
|
||||
columns.CreatedBy: in.CreatedBy,
|
||||
columns.CreatedAt: now,
|
||||
columns.UpdatedBy: in.CreatedBy,
|
||||
columns.UpdatedAt: now,
|
||||
}
|
||||
if len(in.AttachmentUrls) > 0 {
|
||||
data[columns.AttachmentUrls] = gjson.New(in.AttachmentUrls)
|
||||
}
|
||||
if _, err := tx.Ctx(ctx).Model(dao.TWoCommunication.Table()).Data(data).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "create initial work order communication failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sCommonTWoCommunication) Message(ctx context.Context, in *model.TWoMainMessageParam) (err error) {
|
||||
if in == nil {
|
||||
return gerror.New("invalid request")
|
||||
}
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
current, err := getWorkOrder(ctx, in.WoNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
createdDept := contexts.GetDeptId(ctx)
|
||||
createdBy := contexts.GetUserId(ctx)
|
||||
senderName := contexts.GetUserName(ctx)
|
||||
|
||||
// 处理文件上传
|
||||
if in.File != nil {
|
||||
ossResult, err := service.SysOss().Upload(ctx, &model.SysOssUploadParam{
|
||||
File: in.File,
|
||||
FileType: "file",
|
||||
NewFileType: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "file upload failed")
|
||||
}
|
||||
if ossResult != nil && ossResult.Url != "" {
|
||||
in.AttachmentUrls = append(in.AttachmentUrls, ossResult.Url)
|
||||
}
|
||||
}
|
||||
|
||||
// 写入前核对终态;发送人由当前登录用户确定。
|
||||
if current.CurrentStatus == 4 || current.CurrentStatus == 5 {
|
||||
return gerror.New("已完结或已取消的工单不能回复")
|
||||
}
|
||||
content := strings.TrimSpace(in.Content)
|
||||
for _, attachment := range in.AttachmentUrls {
|
||||
var file *entity.SysOss
|
||||
columns := dao.SysOss.Columns()
|
||||
if err = dao.SysOss.Ctx(ctx).Where(columns.TenantId, tenantId).Where(columns.CreatedBy, createdBy).Where(columns.Url, attachment).Scan(&file); err != nil {
|
||||
return gerror.Wrap(err, "校验工单附件失败")
|
||||
}
|
||||
if file == nil || file.OssId == 0 || file.FileSize > 10*1024*1024 {
|
||||
return gerror.New("附件不存在、不属于当前用户或超过10MB")
|
||||
}
|
||||
}
|
||||
if content == "" && len(in.AttachmentUrls) == 0 {
|
||||
return gerror.New("请输入内容或上传附件")
|
||||
}
|
||||
if utf8.RuneCountInString(content) > 2000 || len(in.AttachmentUrls) > 6 {
|
||||
return gerror.New("内容最多2000字,附件最多6个")
|
||||
}
|
||||
isCreator := current.CreatorUserId == createdBy
|
||||
commType := 0
|
||||
if isCreator {
|
||||
commType = 1
|
||||
}
|
||||
now := gtime.Now()
|
||||
communication := do.TWoCommunication{WoNo: current.WoNo, CommType: commType, SenderDeptId: createdDept, SenderUserId: createdBy, SenderName: senderName, Content: content, TenantId: tenantId, CreatedDept: createdDept, CreatedBy: createdBy, UpdatedBy: createdBy}
|
||||
communication.CommNo = fmt.Sprintf("comm%d", gtime.TimestampNano())
|
||||
communication.WoId = current.Id
|
||||
communication.CommTime = now
|
||||
if isCreator {
|
||||
communication.SenderType = current.CreatorType
|
||||
} else {
|
||||
communication.SenderType = 2
|
||||
}
|
||||
if len(in.AttachmentUrls) > 0 {
|
||||
communication.AttachmentUrls = gjson.New(in.AttachmentUrls)
|
||||
}
|
||||
if _, err = dao.TWoCommunication.Ctx(ctx).TX(tx).Data(communication).Insert(); err != nil {
|
||||
return gerror.Wrap(err, "保存工单回复失败")
|
||||
}
|
||||
update := do.TWoMain{UpdatedBy: createdBy}
|
||||
// 创建人在待确认阶段继续反馈视为未解决;客服补充回复不重新打开工单。
|
||||
if isCreator && current.CurrentStatus == workOrderStatusResolved {
|
||||
update.CurrentStatus = workOrderStatusProcessing
|
||||
if err = service.CommonTWoRecord().AddOperation(ctx, &model.TWoRecordAddOperationParam{WoNo: current.WoNo, OperationType: workOrderOperationProcess, BeforeStatus: workOrderStatusResolved, AfterStatus: workOrderStatusProcessing}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !isCreator && current.FirstResponseTime == nil {
|
||||
update.FirstResponseTime = now
|
||||
}
|
||||
if _, err = dao.TWoMain.Ctx(ctx).TX(tx).Where(dao.TWoMain.Columns().Id, current.Id).Where(dao.TWoMain.Columns().TenantId, tenantId).Where(dao.TWoMain.Columns().CurrentStatus, current.CurrentStatus).Data(update).Update(); err != nil {
|
||||
return gerror.Wrap(err, "更新工单失败")
|
||||
}
|
||||
if !isCreator {
|
||||
if err = saveWorkOrderNotice(ctx, tx, ¤t, createdBy, "工单收到回复", content); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wsTag := "ws:tag:wo:communication:" + in.WoNo
|
||||
g.Log().Infof(ctx, "work order communication websocket broadcast tag:%s count:%d", wsTag, websocket.GetTagCount(wsTag))
|
||||
websocket.SendToTag(wsTag, &websocket.WResponse{
|
||||
Event: "ws:event:wo:communication:message",
|
||||
Data: g.Map{
|
||||
"woNo": in.WoNo,
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
func (s *sCommonTWoCommunication) Delete(ctx context.Context, in *model.TWoCommunicationDeleteParam) (err error) {
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "delete work order communication failed")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonTWoCommunication) View(ctx context.Context, in *model.TWoCommunicationViewParam) (res *model.TWoCommunicationViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "get work order communication failed")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rabbitmq"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/websocket"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
||||
"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/do"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
workOrderAutoCloseDelay = 72 * time.Hour
|
||||
|
||||
workOrderStatusAssigned = 1 //已分派
|
||||
workOrderStatusProcessing = 2 //处理中
|
||||
workOrderStatusResolved = 3 //已解决
|
||||
workOrderStatusClosed = 4 //已关单
|
||||
workOrderStatusCanceled = 5 //已取消
|
||||
|
||||
workOrderOperationCreate = 0 //创建
|
||||
workOrderOperationAssign = 1 //分派
|
||||
workOrderOperationProcess = 2 //处理
|
||||
workOrderOperationFinish = 3 //完成
|
||||
workOrderOperationClose = 4 //关单
|
||||
workOrderOperationCancel = 5 //取消
|
||||
)
|
||||
|
||||
type sCommonTWoMain struct{}
|
||||
|
||||
func NewCommonTWoMain() *sCommonTWoMain {
|
||||
return &sCommonTWoMain{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTWoMain(NewCommonTWoMain())
|
||||
}
|
||||
|
||||
func getWorkOrder(ctx context.Context, woNo string) (current entity.TWoMain, err error) {
|
||||
if contexts.GetUserId(ctx) <= 0 || strings.TrimSpace(woNo) == "" {
|
||||
return current, gerror.New("请登录并指定工单编号")
|
||||
}
|
||||
query := dao.TWoMain.Ctx(ctx).Where(dao.TWoMain.Columns().TenantId, contexts.GetTenantId(ctx)).Where(dao.TWoMain.Columns().WoNo, strings.TrimSpace(woNo))
|
||||
if g.DB().GetConfig().Type != "sqlite" {
|
||||
query = query.LockUpdate()
|
||||
}
|
||||
if err = query.Scan(¤t); err != nil {
|
||||
return current, gerror.Wrap(err, "query work order failed")
|
||||
}
|
||||
if current.WoNo == "" {
|
||||
return current, gerror.New("work order not found")
|
||||
}
|
||||
if !contexts.IsSuperAdmin(ctx) && current.CreatorUserId != contexts.GetUserId(ctx) && current.HandlerDeptId != contexts.GetDeptId(ctx) && current.HandlerUserId != contexts.GetUserId(ctx) {
|
||||
return current, gerror.New("无权访问该工单")
|
||||
}
|
||||
return current, nil
|
||||
}
|
||||
|
||||
func (s *sCommonTWoMain) notifyWorkOrderChanged(ctx context.Context, woNo string) {
|
||||
if woNo == "" {
|
||||
return
|
||||
}
|
||||
websocket.SendToTag("ws:tag:wo:communication:"+woNo, &websocket.WResponse{
|
||||
Event: "ws:event:wo:communication:transfer",
|
||||
Data: g.Map{
|
||||
"woNo": woNo,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Model returns the work order main ORM model
|
||||
func (s *sCommonTWoMain) 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.TWoMain.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List returns work order main records.
|
||||
func (s *sCommonTWoMain) List(ctx context.Context, in *model.TWoMainListParam) (list []*model.TWoMainListModel, totalCount int, err error) {
|
||||
if contexts.GetUserId(ctx) <= 0 {
|
||||
return nil, 0, gerror.New("请先登录")
|
||||
}
|
||||
mod := s.Model(ctx, &handler.Option{FilterTenant: true, FilterAuth: false})
|
||||
mod = mod.Fields(model.TWoMainListModel{})
|
||||
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TWoMain.Columns().Id, in.Id)
|
||||
}
|
||||
if in.CurrentStatus > 0 {
|
||||
mod = mod.Where(dao.TWoMain.Columns().CurrentStatus, in.CurrentStatus)
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
mod = mod.WhereBetween(dao.TWoMain.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
if in.WoNo != "" {
|
||||
mod = mod.Where(dao.TWoMain.Columns().WoNo, in.WoNo)
|
||||
}
|
||||
if in.WoType > 0 {
|
||||
mod = mod.Where(dao.TWoMain.Columns().WoType, in.WoType)
|
||||
}
|
||||
if !contexts.IsSuperAdmin(ctx) {
|
||||
columns := dao.TWoMain.Columns()
|
||||
mod = mod.Where("("+columns.CreatorUserId+" = ? OR "+columns.HandlerUserId+" = ? OR "+columns.HandlerDeptId+" = ?)", contexts.GetUserId(ctx), contexts.GetUserId(ctx), contexts.GetDeptId(ctx))
|
||||
}
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
mod = mod.OrderDesc(dao.TWoMain.Columns().Id)
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "get work order list failed")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonTWoMain) Export(ctx context.Context, in *model.TWoMainListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tags, err := convert.GetEntityDescTags(model.TWoMainExportModel{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
fileName = "export-work-order-main-" + gctx.CtxId(ctx)
|
||||
sheetName = fmt.Sprintf("total %v rows, %v pages, page %v, current %v rows", totalCount, (totalCount+in.PageSize-1)/in.PageSize, in.Page, len(list))
|
||||
exports []model.TWoMainExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// 添加工单
|
||||
func (s *sCommonTWoMain) Edit(ctx context.Context, in *model.TWoMainEditParam) (err error) {
|
||||
|
||||
if in.Id > 0 && in.WoNo != nil {
|
||||
err = xgorm.IsUnique(ctx, &dao.TWoMain, g.Map{dao.TWoMain.Columns().WoNo: in.WoNo}, "work order number exists", in.Id)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
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).
|
||||
WherePri(in.Id).
|
||||
Fields(model.TWoMainUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "update work order failed")
|
||||
}
|
||||
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()
|
||||
woNo := fmt.Sprintf("gd%d", gtime.TimestampMilli())
|
||||
in.WoNo = &woNo
|
||||
in.CreatorDeptId = &createdDept
|
||||
in.CreatorUserId = &createdBy
|
||||
creatorName := contexts.GetUserName(ctx)
|
||||
in.CreatorName = &creatorName
|
||||
currentStatus := workOrderStatusProcessing
|
||||
in.CurrentStatus = ¤tStatus
|
||||
|
||||
// 查询当前部门信息
|
||||
var currentDept entity.SysDept
|
||||
err = dao.SysDept.Ctx(ctx).
|
||||
Where(dao.SysDept.Columns().DeptId, createdDept).
|
||||
Scan(¤tDept)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 分派到上级部门
|
||||
var handlerDeptId int64
|
||||
var handlerUserId int64
|
||||
if currentDept.ParentId != 0 && currentDept.ParentId != 100 {
|
||||
// 如果有上级部门,分派到上级部门
|
||||
handlerDeptId = currentDept.ParentId
|
||||
var parentDept entity.SysDept
|
||||
err = dao.SysDept.Ctx(ctx).
|
||||
Where(dao.SysDept.Columns().DeptId, handlerDeptId).
|
||||
Scan(&parentDept)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handlerUserId = gconv.Int64(parentDept.Leader)
|
||||
} else {
|
||||
// 如果没有上级部门,分派到当前部门
|
||||
handlerDeptId = createdDept
|
||||
handlerUserId = gconv.Int64(currentDept.Leader)
|
||||
}
|
||||
|
||||
in.HandlerDeptId = &handlerDeptId
|
||||
in.HandlerUserId = &handlerUserId
|
||||
if _, err = tx.Ctx(ctx).Model(dao.TWoMain.Table()).
|
||||
Fields(model.TWoMainInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "create work order failed")
|
||||
return err
|
||||
}
|
||||
content := ""
|
||||
if in.Content != nil {
|
||||
content = *in.Content
|
||||
}
|
||||
attachmentUrls := append([]string{}, in.AttachmentUrls...)
|
||||
if len(attachmentUrls) == 0 && in.Attachment != nil {
|
||||
attachment := strings.TrimSpace(*in.Attachment)
|
||||
if attachment != "" {
|
||||
attachmentUrls = append(attachmentUrls, attachment)
|
||||
}
|
||||
}
|
||||
creatorType := 0
|
||||
if in.CreatorType != nil {
|
||||
creatorType = *in.CreatorType
|
||||
}
|
||||
if err = service.CommonTWoCommunication().CreateInitialMessage(ctx, tx, &model.TWoCommunicationInitialMessageParam{
|
||||
WoNo: *in.WoNo,
|
||||
CommType: 1,
|
||||
SenderType: creatorType,
|
||||
SenderDeptId: createdDept,
|
||||
SenderUserId: createdBy,
|
||||
SenderName: creatorName,
|
||||
Content: content,
|
||||
AttachmentUrls: attachmentUrls,
|
||||
TenantId: tenantId,
|
||||
CreatedDept: createdDept,
|
||||
CreatedBy: createdBy,
|
||||
CreatedAt: in.CreatedAt,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return service.CommonTWoRecord().AddOperation(ctx, &model.TWoRecordAddOperationParam{
|
||||
WoNo: *in.WoNo,
|
||||
OperationType: workOrderOperationCreate,
|
||||
})
|
||||
})
|
||||
}
|
||||
func (s *sCommonTWoMain) Message(ctx context.Context, in *model.TWoMainMessageParam) (err error) {
|
||||
return service.CommonTWoCommunication().Message(ctx, in)
|
||||
}
|
||||
|
||||
// 转单:转给顶级部门(ParentId=0)的负责人
|
||||
func (s *sCommonTWoMain) Transfer(ctx context.Context, in *model.TWoMainTransferParam) (err error) {
|
||||
if in == nil {
|
||||
return gerror.New("invalid request")
|
||||
}
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
current, err := getWorkOrder(ctx, in.WoNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if current.CurrentStatus != workOrderStatusAssigned && current.CurrentStatus != workOrderStatusProcessing {
|
||||
return gerror.New("工单状态不允许此操作")
|
||||
}
|
||||
if current.CreatorUserId == contexts.GetUserId(ctx) && !contexts.IsSuperAdmin(ctx) {
|
||||
return gerror.New("请由处理人员执行此操作")
|
||||
}
|
||||
updatedBy := contexts.GetUserId(ctx)
|
||||
|
||||
// 1. 查找顶级部门(ParentId = 0)
|
||||
var topDept entity.SysDept
|
||||
err = dao.SysDept.Ctx(ctx).
|
||||
Where(dao.SysDept.Columns().ParentId, 0).
|
||||
Where(dao.SysDept.Columns().TenantId, current.TenantId).
|
||||
OrderAsc(dao.SysDept.Columns().OrderNum).OrderAsc(dao.SysDept.Columns().DeptId).
|
||||
Scan(&topDept)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "查询顶级部门失败")
|
||||
}
|
||||
if topDept.DeptId == 0 {
|
||||
return gerror.New("未找到顶级部门")
|
||||
}
|
||||
|
||||
// 3. 更新工单,转给顶级部门及其负责人
|
||||
if _, err = dao.TWoMain.Ctx(ctx).TX(tx).Where(dao.TWoMain.Columns().TenantId, current.TenantId).Where(dao.TWoMain.Columns().CurrentStatus, current.CurrentStatus).
|
||||
Where(dao.TWoMain.Columns().WoNo, in.WoNo).
|
||||
Data(do.TWoMain{
|
||||
AssignDeptId: topDept.DeptId,
|
||||
AssignUserId: topDept.Leader,
|
||||
HandlerDeptId: topDept.DeptId,
|
||||
HandlerUserId: topDept.Leader,
|
||||
CurrentStatus: workOrderStatusProcessing,
|
||||
UpdatedBy: updatedBy,
|
||||
}).Update(); err != nil {
|
||||
return gerror.Wrap(err, "转单失败")
|
||||
}
|
||||
|
||||
// 4. 记录操作日志
|
||||
operationType := workOrderOperationAssign
|
||||
beforeStatus := current.CurrentStatus
|
||||
afterStatus := workOrderStatusProcessing
|
||||
return service.CommonTWoRecord().AddOperation(ctx, &model.TWoRecordAddOperationParam{
|
||||
WoNo: in.WoNo,
|
||||
OperationType: operationType,
|
||||
OperationDeptId: topDept.DeptId,
|
||||
OperationUserId: topDept.Leader,
|
||||
TargetDeptId: topDept.DeptId,
|
||||
BeforeStatus: beforeStatus,
|
||||
AfterStatus: afterStatus,
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.notifyWorkOrderChanged(ctx, in.WoNo)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解决
|
||||
func (s *sCommonTWoMain) Resolve(ctx context.Context, in *model.TWoMainResolveParam) (err error) {
|
||||
if in == nil {
|
||||
return gerror.New("invalid request")
|
||||
}
|
||||
var resolvedAt *gtime.Time
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
current, err := getWorkOrder(ctx, in.WoNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if current.CurrentStatus != workOrderStatusAssigned && current.CurrentStatus != workOrderStatusProcessing {
|
||||
return gerror.New("工单状态不允许此操作")
|
||||
}
|
||||
if current.CreatorUserId == contexts.GetUserId(ctx) && !contexts.IsSuperAdmin(ctx) {
|
||||
return gerror.New("请由处理人员执行此操作")
|
||||
}
|
||||
updatedBy := contexts.GetUserId(ctx)
|
||||
creatorDept := contexts.GetDeptId(ctx)
|
||||
now := gtime.Now()
|
||||
if _, err = dao.TWoMain.Ctx(ctx).TX(tx).Where(dao.TWoMain.Columns().TenantId, current.TenantId).Where(dao.TWoMain.Columns().CurrentStatus, current.CurrentStatus).
|
||||
Where(dao.TWoMain.Columns().WoNo, in.WoNo).
|
||||
Data(do.TWoMain{
|
||||
CurrentStatus: workOrderStatusResolved,
|
||||
ResolveTime: now,
|
||||
HandlerDeptId: creatorDept,
|
||||
HandlerUserId: updatedBy,
|
||||
UpdatedBy: updatedBy,
|
||||
}).Update(); err != nil {
|
||||
return gerror.Wrap(err, "operation failed")
|
||||
}
|
||||
if err = service.CommonTWoRecord().AddOperation(ctx, &model.TWoRecordAddOperationParam{
|
||||
WoNo: in.WoNo,
|
||||
OperationType: workOrderOperationFinish,
|
||||
BeforeStatus: current.CurrentStatus,
|
||||
AfterStatus: workOrderStatusResolved,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = saveWorkOrderNotice(ctx, tx, ¤t, updatedBy, "工单待确认解决", "问题已标记解决,请在72小时内确认或反馈未解决。"); err != nil {
|
||||
return err
|
||||
}
|
||||
resolvedAt = now
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resolvedAt != nil {
|
||||
s.publishAutoClose(ctx, in.WoNo, resolvedAt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sCommonTWoMain) publishAutoClose(ctx context.Context, woNo string, resolvedAt *gtime.Time) {
|
||||
if !rabbitmq.Enabled(ctx) {
|
||||
g.Log().Errorf(ctx, "work order auto close skipped, rabbitmq disabled, woNo:%s", woNo)
|
||||
return
|
||||
}
|
||||
message := model.WorkOrderAutoCloseMessage{
|
||||
WoNo: woNo,
|
||||
ResolveTimeTs: resolvedAt.Timestamp(),
|
||||
}
|
||||
if err := rabbitmq.PublishDelayed(ctx, consts.QueueWorkOrderAutoCloseDelay, consts.QueueWorkOrderAutoClose, workOrderAutoCloseDelay, message); err != nil {
|
||||
g.Log().Errorf(ctx, "publish work order auto close failed, woNo:%s, err:%+v", woNo, err)
|
||||
return
|
||||
}
|
||||
g.Log().Infof(ctx, "work order auto close published, woNo:%s, delay:%s", woNo, workOrderAutoCloseDelay)
|
||||
}
|
||||
|
||||
func (s *sCommonTWoMain) AutoCloseResolved(ctx context.Context, message model.WorkOrderAutoCloseMessage) error {
|
||||
if message.WoNo == "" {
|
||||
return nil
|
||||
}
|
||||
g.Log().Infof(ctx, "work order auto close received, woNo:%s", message.WoNo)
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
current := entity.TWoMain{}
|
||||
if err := tx.Ctx(ctx).Model(dao.TWoMain.Table()).
|
||||
Where(dao.TWoMain.Columns().WoNo, message.WoNo).
|
||||
Scan(¤t); err != nil {
|
||||
return gerror.Wrap(err, "query work order failed")
|
||||
}
|
||||
if current.WoNo == "" || current.CurrentStatus != workOrderStatusResolved {
|
||||
return nil
|
||||
}
|
||||
if current.ResolveTime == nil || current.ResolveTime.Add(72*time.Hour).After(gtime.Now()) {
|
||||
return nil
|
||||
}
|
||||
if message.ResolveTimeTs > 0 && (current.ResolveTime == nil || current.ResolveTime.Timestamp() != message.ResolveTimeTs) {
|
||||
g.Log().Infof(ctx, "work order auto close skipped, resolve time changed, woNo:%s, messageResolveTime:%d", message.WoNo, message.ResolveTimeTs)
|
||||
return nil
|
||||
}
|
||||
|
||||
operatorID := int64(0)
|
||||
now := gtime.Now()
|
||||
result, err := tx.Ctx(ctx).Model(dao.TWoMain.Table()).
|
||||
Where(dao.TWoMain.Columns().Id, current.Id).
|
||||
Where(dao.TWoMain.Columns().TenantId, current.TenantId).
|
||||
Where(dao.TWoMain.Columns().ResolveTime, current.ResolveTime).
|
||||
Where(dao.TWoMain.Columns().CurrentStatus, workOrderStatusResolved).
|
||||
Data(do.TWoMain{
|
||||
CurrentStatus: workOrderStatusClosed,
|
||||
CloseTime: now,
|
||||
UpdatedBy: operatorID,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "auto close work order failed")
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil || affected == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.Ctx(ctx).Model(dao.TWoRecord.Table()).Data(do.TWoRecord{
|
||||
WoNo: message.WoNo,
|
||||
OperationType: workOrderOperationClose,
|
||||
OperationDeptId: current.HandlerDeptId,
|
||||
OperationUserId: operatorID,
|
||||
BeforeStatus: workOrderStatusResolved,
|
||||
AfterStatus: workOrderStatusClosed,
|
||||
TenantId: current.TenantId,
|
||||
CreatedDept: current.CreatedDept,
|
||||
CreatedBy: operatorID,
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "保存自动完结记录失败")
|
||||
}
|
||||
return saveWorkOrderNotice(ctx, tx, ¤t, 0, "工单已自动完结", "问题标记解决超过72小时,工单已自动完结。")
|
||||
})
|
||||
}
|
||||
|
||||
// 关单
|
||||
func (s *sCommonTWoMain) Close(ctx context.Context, in *model.TWoMainCloseParam) (err error) {
|
||||
if in == nil {
|
||||
return gerror.New("invalid request")
|
||||
}
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
current, err := getWorkOrder(ctx, in.WoNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if current.CurrentStatus != workOrderStatusResolved {
|
||||
return gerror.New("工单状态不允许此操作")
|
||||
}
|
||||
updatedBy := contexts.GetUserId(ctx)
|
||||
now := gtime.Now()
|
||||
if _, err = dao.TWoMain.Ctx(ctx).TX(tx).Where(dao.TWoMain.Columns().TenantId, current.TenantId).Where(dao.TWoMain.Columns().CurrentStatus, current.CurrentStatus).
|
||||
Where(dao.TWoMain.Columns().WoNo, in.WoNo).
|
||||
Data(do.TWoMain{
|
||||
CurrentStatus: workOrderStatusClosed,
|
||||
CloseTime: now,
|
||||
UpdatedBy: updatedBy,
|
||||
}).Update(); err != nil {
|
||||
return gerror.Wrap(err, "operation failed")
|
||||
}
|
||||
return service.CommonTWoRecord().AddOperation(ctx, &model.TWoRecordAddOperationParam{
|
||||
WoNo: in.WoNo,
|
||||
OperationType: workOrderOperationClose,
|
||||
BeforeStatus: current.CurrentStatus,
|
||||
AfterStatus: workOrderStatusClosed,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 取消
|
||||
func (s *sCommonTWoMain) Cancel(ctx context.Context, in *model.TWoMainCancelParam) (err error) {
|
||||
if in == nil {
|
||||
return gerror.New("invalid request")
|
||||
}
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
current, err := getWorkOrder(ctx, in.WoNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if current.CurrentStatus != workOrderStatusAssigned && current.CurrentStatus != workOrderStatusProcessing {
|
||||
return gerror.New("工单状态不允许此操作")
|
||||
}
|
||||
if current.CreatorUserId != contexts.GetUserId(ctx) && !contexts.IsSuperAdmin(ctx) {
|
||||
return gerror.New("只有创建人可以取消工单")
|
||||
}
|
||||
updatedBy := contexts.GetUserId(ctx)
|
||||
now := gtime.Now()
|
||||
if _, err = dao.TWoMain.Ctx(ctx).TX(tx).Where(dao.TWoMain.Columns().TenantId, current.TenantId).Where(dao.TWoMain.Columns().CurrentStatus, current.CurrentStatus).
|
||||
Where(dao.TWoMain.Columns().WoNo, in.WoNo).
|
||||
Data(do.TWoMain{
|
||||
CurrentStatus: workOrderStatusCanceled,
|
||||
CloseTime: now,
|
||||
UpdatedBy: updatedBy,
|
||||
}).Update(); err != nil {
|
||||
return gerror.Wrap(err, "operation failed")
|
||||
}
|
||||
return service.CommonTWoRecord().AddOperation(ctx, &model.TWoRecordAddOperationParam{
|
||||
WoNo: in.WoNo,
|
||||
OperationType: workOrderOperationCancel,
|
||||
BeforeStatus: current.CurrentStatus,
|
||||
AfterStatus: workOrderStatusCanceled,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 评价
|
||||
func (s *sCommonTWoMain) Evaluate(ctx context.Context, in *model.TWoMainEvaluateParam) error {
|
||||
if in == nil || in.SatisfactionLevel < 1 || in.SatisfactionLevel > 5 {
|
||||
return gerror.New("请选择1至5星评价")
|
||||
}
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
current, err := getWorkOrder(ctx, in.WoNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userID := contexts.GetUserId(ctx)
|
||||
if current.CreatorUserId != userID || current.CurrentStatus != workOrderStatusClosed || current.EvalTime != nil || current.SatisfactionLevel != 0 {
|
||||
return gerror.New("只有创建人可以对已完结工单评价一次")
|
||||
}
|
||||
result, err := dao.TWoMain.Ctx(ctx).TX(tx).Where(dao.TWoMain.Columns().Id, current.Id).Where(dao.TWoMain.Columns().TenantId, current.TenantId).WhereNull(dao.TWoMain.Columns().EvalTime).Data(do.TWoMain{SatisfactionLevel: in.SatisfactionLevel, EvalContent: in.EvalContent, EvalTime: gtime.Now(), UpdatedBy: userID}).Update()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "保存评价失败")
|
||||
}
|
||||
count, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count != 1 {
|
||||
return gerror.New("工单已评价,请刷新")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *sCommonTWoMain) Delete(ctx context.Context, in *model.TWoMainDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "get work order list failed")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View returns a work order main record.
|
||||
func (s *sCommonTWoMain) View(ctx context.Context, in *model.TWoMainViewParam) (res *model.TWoMainViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "get work order list failed")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
// Package gen
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2026 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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/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/service"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type sCommonTWoRecord struct{}
|
||||
|
||||
func NewCommonTWoRecord() *sCommonTWoRecord {
|
||||
return &sCommonTWoRecord{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonTWoRecord(NewCommonTWoRecord())
|
||||
}
|
||||
|
||||
// Model 工单记录表ORM模型
|
||||
func (s *sCommonTWoRecord) 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.TWoRecord.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取工单记录表列表
|
||||
func (s *sCommonTWoRecord) List(ctx context.Context, in *model.TWoRecordListParam) (list []*model.TWoRecordListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: true,
|
||||
})
|
||||
mod = mod.Fields(model.TWoRecordListModel{})
|
||||
|
||||
// 直接使用 dao 创建 model,避免自动 handler 对关联表添加软删除条件
|
||||
mod = s.Model(ctx)
|
||||
|
||||
// 手动添加租户过滤
|
||||
tenantId := contexts.GetTenantId(ctx)
|
||||
if tenantId != "" {
|
||||
mod = mod.Where(dao.TWoRecord.Columns().TenantId, tenantId)
|
||||
}
|
||||
|
||||
cardTable := dao.TWoRecord.Table()
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.FieldsPrefix(cardTable, model.TWoRecordListModel{})
|
||||
mod = mod.Fields("op_dept.dept_name AS operation_dept_name")
|
||||
mod = mod.Fields("op_user.user_name AS operation_user_name")
|
||||
mod = mod.Fields("t_dept.dept_name AS target_dept_name")
|
||||
mod = mod.Fields("t_user.user_name AS target_user_name")
|
||||
|
||||
// 左关联部门表获取操作机构名称
|
||||
mod = mod.LeftJoin("sys_dept AS op_dept ON op_dept.dept_id = " + cardTable + ".operation_dept_id")
|
||||
|
||||
// 左关联用户表获取操作用户名称
|
||||
mod = mod.LeftJoin("sys_user AS op_user ON op_user.user_id = " + cardTable + ".operation_user_id")
|
||||
|
||||
// 左关联部门表获取目标部门名称
|
||||
mod = mod.LeftJoin("sys_dept AS t_dept ON t_dept.dept_id = " + cardTable + ".target_dept_id")
|
||||
|
||||
// 左关联用户表获取目标用户名称
|
||||
mod = mod.LeftJoin("sys_user AS t_user ON t_user.user_id = " + cardTable + ".target_user_id")
|
||||
|
||||
// 查询主键ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TWoRecord.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询变更前状态
|
||||
if in.BeforeStatus > 0 {
|
||||
mod = mod.Where(dao.TWoRecord.Columns().BeforeStatus, in.BeforeStatus)
|
||||
}
|
||||
|
||||
// 查询变更后状态
|
||||
if in.AfterStatus > 0 {
|
||||
mod = mod.Where(dao.TWoRecord.Columns().AfterStatus, in.AfterStatus)
|
||||
}
|
||||
|
||||
// 查询工单编号
|
||||
if in.WoNo != "" {
|
||||
mod = mod.Where(dao.TWoRecord.Columns().WoNo, in.WoNo)
|
||||
}
|
||||
|
||||
// 查询操作类型
|
||||
if in.OperationType > 0 {
|
||||
mod = mod.Where(dao.TWoRecord.Columns().OperationType, in.OperationType)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
}
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TWoRecord.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PageSize)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderDesc(dao.TWoRecord.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 *sCommonTWoRecord) Export(ctx context.Context, in *model.TWoRecordListParam) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(model.TWoRecordExportModel{})
|
||||
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.TWoRecordExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增工单记录表
|
||||
func (s *sCommonTWoRecord) Edit(ctx context.Context, in *model.TWoRecordEditParam) (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.TWoRecordUpdateFields{}).
|
||||
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.TWoRecordInsertFields{}).
|
||||
Data(in).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增工单记录表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// 添加工单记录表
|
||||
func (s *sCommonTWoRecord) AddOperation(ctx context.Context, in *model.TWoRecordAddOperationParam) (err error) {
|
||||
operationDeptId := contexts.GetDeptId(ctx)
|
||||
operationUserId := contexts.GetUserId(ctx)
|
||||
param := &model.TWoRecordEditParam{
|
||||
WoNo: &in.WoNo,
|
||||
OperationType: &in.OperationType,
|
||||
OperationDeptId: &operationDeptId,
|
||||
OperationUserId: &operationUserId,
|
||||
}
|
||||
if in.TargetDeptId > 0 {
|
||||
param.TargetDeptId = &in.TargetDeptId
|
||||
param.TargetUserId = &in.TargetUserId
|
||||
}
|
||||
if in.BeforeStatus > 0 {
|
||||
param.BeforeStatus = &in.BeforeStatus
|
||||
}
|
||||
if in.AfterStatus > 0 {
|
||||
param.AfterStatus = &in.AfterStatus
|
||||
}
|
||||
return s.Edit(ctx, param)
|
||||
}
|
||||
|
||||
func (s *sCommonTWoRecord) Delete(ctx context.Context, in *model.TWoRecordDeleteParam) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除工单记录表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取工单记录表指定信息
|
||||
func (s *sCommonTWoRecord) View(ctx context.Context, in *model.TWoRecordViewParam) (res *model.TWoRecordViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取工单记录表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// saveWorkOrderNotice 与工单操作使用同一事务,通知失败时回滚,避免遗漏。
|
||||
func saveWorkOrderNotice(ctx context.Context, tx gdb.TX, current *entity.TWoMain, operator int64, title, content string) error {
|
||||
if current.CreatorUserId <= 0 || current.CreatorUserId == operator {
|
||||
return nil
|
||||
}
|
||||
id, err := dao.TMessageLog.Ctx(ctx).TX(tx).Data(do.TMessageLog{MessageType: 1, SendUserType: current.CreatorType, UserIds: gjson.New([]int64{current.CreatorUserId}), MessageTitle: title, MessageContent: content, MessageLink: "/pages/business/views/workOrderDetail/index?woNo=" + url.QueryEscape(current.WoNo), SendTime: gtime.Now(), IsRecall: 0, TenantId: current.TenantId, CreatedDept: current.CreatorDeptId, CreatedBy: operator}).InsertAndGetId()
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "保存工单通知失败")
|
||||
}
|
||||
_, err = dao.TUserMessage.Ctx(ctx).TX(tx).Data(do.TUserMessage{MessageLogId: id, UserId: current.CreatorUserId, IsRead: 0, TenantId: current.TenantId, CreatedDept: current.CreatorDeptId, CreatedBy: operator}).Insert()
|
||||
return gerror.Wrap(err, "保存工单用户通知失败")
|
||||
}
|
||||
Reference in New Issue
Block a user