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,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
|
||||
}
|
||||
Reference in New Issue
Block a user