feat: add balance and ledger endpoints for tenant

- Implemented MyBalance and MyLedgerPage methods in the ledger service to retrieve current user balance and transaction history for a specified tenant.
- Added corresponding test cases for MyBalance and MyLedgerPage methods in the ledger test suite.
- Created DTOs for balance response and ledger items to structure the response data.
- Updated Swagger documentation to include new endpoints for retrieving tenant balance and ledgers.
- Added HTTP tests for the new endpoints to ensure proper functionality.
This commit is contained in:
2025-12-18 16:24:37 +08:00
parent 435e541dbe
commit 3249e405ac
13 changed files with 990 additions and 33 deletions

View File

@@ -6,10 +6,14 @@ import (
"time"
"quyun/v2/app/errorx"
"quyun/v2/app/http/tenant/dto"
"quyun/v2/app/requests"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
"github.com/samber/lo"
"github.com/sirupsen/logrus"
"go.ipao.vip/gen"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
@@ -29,6 +33,84 @@ type ledger struct {
db *gorm.DB
}
// MyBalance 查询当前用户在指定租户下的余额信息(可用/冻结)。
func (s *ledger) MyBalance(ctx context.Context, tenantID, userID int64) (*models.TenantUser, error) {
if tenantID <= 0 || userID <= 0 {
return nil, errorx.ErrInvalidParameter.WithMsg("tenant_id/user_id must be > 0")
}
logrus.WithFields(logrus.Fields{
"tenant_id": tenantID,
"user_id": userID,
}).Info("services.ledger.me.balance")
tbl, query := models.TenantUserQuery.QueryContext(ctx)
m, err := query.Where(tbl.TenantID.Eq(tenantID), tbl.UserID.Eq(userID)).First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errorx.ErrRecordNotFound.WithMsg("tenant user not found")
}
return nil, err
}
return m, nil
}
// MyLedgerPage 分页查询当前用户在指定租户下的余额流水(用于“我的流水”)。
func (s *ledger) MyLedgerPage(ctx context.Context, tenantID, userID int64, filter *dto.MyLedgerListFilter) (*requests.Pager, error) {
if tenantID <= 0 || userID <= 0 {
return nil, errorx.ErrInvalidParameter.WithMsg("tenant_id/user_id must be > 0")
}
if filter == nil {
filter = &dto.MyLedgerListFilter{}
}
logrus.WithFields(logrus.Fields{
"tenant_id": tenantID,
"user_id": userID,
"type": lo.FromPtr(filter.Type),
"order_id": lo.FromPtr(filter.OrderID),
}).Info("services.ledger.me.ledgers.page")
filter.Pagination.Format()
tbl, query := models.TenantLedgerQuery.QueryContext(ctx)
conds := []gen.Condition{
tbl.TenantID.Eq(tenantID),
tbl.UserID.Eq(userID),
}
if filter.Type != nil {
conds = append(conds, tbl.Type.Eq(*filter.Type))
}
if filter.OrderID != nil && *filter.OrderID > 0 {
conds = append(conds, tbl.OrderID.Eq(*filter.OrderID))
}
if filter.CreatedAtFrom != nil {
conds = append(conds, tbl.CreatedAt.Gte(*filter.CreatedAtFrom))
}
if filter.CreatedAtTo != nil {
conds = append(conds, tbl.CreatedAt.Lte(*filter.CreatedAtTo))
}
ledgers, total, err := query.Where(conds...).Order(tbl.ID.Desc()).FindByPage(int(filter.Offset()), int(filter.Limit))
if err != nil {
return nil, err
}
items := lo.Map(ledgers, func(m *models.TenantLedger, _ int) *dto.MyLedgerItem {
return &dto.MyLedgerItem{
Ledger: m,
TypeDescription: m.Type.Description(),
}
})
return &requests.Pager{
Pagination: filter.Pagination,
Total: total,
Items: items,
}, nil
}
// Freeze 将可用余额转入冻结余额,并写入账本记录。
func (s *ledger) Freeze(ctx context.Context, tenantID, userID, orderID, amount int64, idempotencyKey, remark string, now time.Time) (*LedgerApplyResult, error) {
return s.apply(ctx, s.db, tenantID, userID, orderID, consts.TenantLedgerTypeFreeze, amount, -amount, amount, idempotencyKey, remark, now)