统一金额分制重构

This commit is contained in:
yml
2026-06-09 19:04:11 +08:00
parent 87673288ef
commit 5cd3ead4bd
69 changed files with 886 additions and 1277 deletions
+17 -18
View File
@@ -3,30 +3,29 @@ package adminuser
import "time"
type UserDTO struct {
ID uint64 `json:"id"`
Phone string `json:"phone"`
Nickname string `json:"nickname"`
RealnameStatus string `json:"realname_status"`
RiskStatus string `json:"risk_status"`
CreditScore int `json:"credit_score"`
DepositFreeQuota float64 `json:"deposit_free_quota"`
DepositFreeUsed float64 `json:"deposit_free_used"`
DepositFreeRemaining float64 `json:"deposit_free_remaining"`
Status string `json:"status"`
OrderCount int64 `json:"order_count"`
ListingCount int64 `json:"listing_count"`
DisputeCount int64 `json:"dispute_count"`
LastLoginAt *time.Time `json:"last_login_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID uint64 `json:"id"`
Phone string `json:"phone"`
Nickname string `json:"nickname"`
RealnameStatus string `json:"realname_status"`
RiskStatus string `json:"risk_status"`
CreditScore int `json:"credit_score"`
DepositFreeQuotaCent int64 `json:"deposit_free_quota_cent"`
DepositFreeUsedCent int64 `json:"deposit_free_used_cent"`
DepositFreeRemainingCent int64 `json:"deposit_free_remaining_cent"`
Status string `json:"status"`
OrderCount int64 `json:"order_count"`
ListingCount int64 `json:"listing_count"`
DisputeCount int64 `json:"dispute_count"`
LastLoginAt *time.Time `json:"last_login_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type FreezeRequest struct {
Reason string `json:"reason"`
}
type DepositFreeQuotaRequest struct {
AmountCent int64 `json:"amount_cent"`
Amount float64 `json:"amount"`
AmountCent int64 `json:"amount_cent"`
}
type PaginatedResult struct {
@@ -2,11 +2,9 @@ package adminuser
import (
"errors"
"math"
"hfb_sys/backend/internal/auditlog"
"hfb_sys/backend/internal/model"
"hfb_sys/backend/pkg/money"
"gorm.io/gorm"
"gorm.io/gorm/clause"
@@ -34,11 +32,11 @@ func (r *Repository) List(page, pageSize int) (*PaginatedResult, error) {
COALESCE(o.order_count, 0) AS order_count,
COALESCE(l.listing_count, 0) AS listing_count,
COALESCE(d.dispute_count, 0) AS dispute_count,
COALESCE(df.deposit_free_used, 0) AS deposit_free_used`).
COALESCE(df.deposit_free_used_cent, 0) AS deposit_free_used_cent`).
Joins("LEFT JOIN (SELECT user_id, COUNT(*) AS order_count FROM (SELECT renter_id AS user_id FROM rental_orders UNION ALL SELECT owner_id AS user_id FROM rental_orders) AS order_users GROUP BY user_id) AS o ON o.user_id = u.id").
Joins("LEFT JOIN (SELECT owner_id AS user_id, COUNT(*) AS listing_count FROM rental_listings GROUP BY owner_id) AS l ON l.user_id = u.id").
Joins("LEFT JOIN (SELECT user_id, COUNT(*) AS dispute_count FROM (SELECT initiator_id AS user_id FROM disputes UNION ALL SELECT target_user_id AS user_id FROM disputes) AS dispute_users GROUP BY user_id) AS d ON d.user_id = u.id").
Joins("LEFT JOIN (SELECT renter_id AS user_id, SUM(deposit_waived_amount) AS deposit_free_used FROM rental_orders WHERE status NOT IN ('completed', 'cancelled', 'closed') GROUP BY renter_id) AS df ON df.user_id = u.id").
Joins("LEFT JOIN (SELECT renter_id AS user_id, SUM(deposit_waived_amount_cent) AS deposit_free_used_cent FROM rental_orders WHERE status NOT IN ('completed', 'cancelled', 'closed') GROUP BY renter_id) AS df ON df.user_id = u.id").
Order("u.id DESC").
Offset(offset).Limit(pageSize).
Scan(&rows).Error
@@ -65,23 +63,18 @@ func (r *Repository) SetDepositFreeQuota(adminID uint64, userID uint64, req Depo
if amountCent < 0 {
return nil, ErrInvalidUser
}
amount := float64(amountCent) / 100
err := r.db.Transaction(func(tx *gorm.DB) error {
var user model.User
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&user, userID).Error; err != nil {
return err
}
beforeAmount := user.DepositFreeQuota
beforeAmountCent := user.DepositFreeQuotaCent
user.DepositFreeQuota = amount
user.DepositFreeQuotaCent = amountCent
if err := tx.Save(&user).Error; err != nil {
return err
}
return appendAuditLog(tx, adminID, "admin_user.set_deposit_free_quota", user.ID, meta, map[string]any{
"user_id": user.ID,
"before_amount": beforeAmount,
"after_amount": amount,
"before_amount_cent": beforeAmountCent,
"after_amount_cent": amountCent,
})
@@ -127,11 +120,11 @@ func (r *Repository) Find(userID uint64) (*UserDTO, error) {
COALESCE(o.order_count, 0) AS order_count,
COALESCE(l.listing_count, 0) AS listing_count,
COALESCE(d.dispute_count, 0) AS dispute_count,
COALESCE(df.deposit_free_used, 0) AS deposit_free_used`).
COALESCE(df.deposit_free_used_cent, 0) AS deposit_free_used_cent`).
Joins("LEFT JOIN (SELECT user_id, COUNT(*) AS order_count FROM (SELECT renter_id AS user_id FROM rental_orders UNION ALL SELECT owner_id AS user_id FROM rental_orders) AS order_users GROUP BY user_id) AS o ON o.user_id = u.id").
Joins("LEFT JOIN (SELECT owner_id AS user_id, COUNT(*) AS listing_count FROM rental_listings GROUP BY owner_id) AS l ON l.user_id = u.id").
Joins("LEFT JOIN (SELECT user_id, COUNT(*) AS dispute_count FROM (SELECT initiator_id AS user_id FROM disputes UNION ALL SELECT target_user_id AS user_id FROM disputes) AS dispute_users GROUP BY user_id) AS d ON d.user_id = u.id").
Joins("LEFT JOIN (SELECT renter_id AS user_id, SUM(deposit_waived_amount) AS deposit_free_used FROM rental_orders WHERE status NOT IN ('completed', 'cancelled', 'closed') GROUP BY renter_id) AS df ON df.user_id = u.id").
Joins("LEFT JOIN (SELECT renter_id AS user_id, SUM(deposit_waived_amount_cent) AS deposit_free_used_cent FROM rental_orders WHERE status NOT IN ('completed', 'cancelled', 'closed') GROUP BY renter_id) AS df ON df.user_id = u.id").
Where("u.id = ?", userID).
First(&row).Error
if err != nil {
@@ -143,46 +136,39 @@ func (r *Repository) Find(userID uint64) (*UserDTO, error) {
type userRow struct {
model.User
OrderCount int64
ListingCount int64
DisputeCount int64
DepositFreeUsed float64
OrderCount int64
ListingCount int64
DisputeCount int64
DepositFreeUsedCent int64
}
func (row userRow) toDTO() UserDTO {
remaining := roundMoney(row.DepositFreeQuota - row.DepositFreeUsed)
remaining := row.DepositFreeQuotaCent - row.DepositFreeUsedCent
if remaining < 0 {
remaining = 0
}
return UserDTO{
ID: row.ID,
Phone: row.Phone,
Nickname: row.Nickname,
RealnameStatus: row.RealnameStatus,
RiskStatus: row.RiskStatus,
CreditScore: row.CreditScore,
DepositFreeQuota: row.DepositFreeQuota,
DepositFreeUsed: roundMoney(row.DepositFreeUsed),
DepositFreeRemaining: remaining,
Status: row.Status,
OrderCount: row.OrderCount,
ListingCount: row.ListingCount,
DisputeCount: row.DisputeCount,
LastLoginAt: row.LastLoginAt,
CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt,
ID: row.ID,
Phone: row.Phone,
Nickname: row.Nickname,
RealnameStatus: row.RealnameStatus,
RiskStatus: row.RiskStatus,
CreditScore: row.CreditScore,
DepositFreeQuotaCent: row.DepositFreeQuotaCent,
DepositFreeUsedCent: row.DepositFreeUsedCent,
DepositFreeRemainingCent: remaining,
Status: row.Status,
OrderCount: row.OrderCount,
ListingCount: row.ListingCount,
DisputeCount: row.DisputeCount,
LastLoginAt: row.LastLoginAt,
CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt,
}
}
func roundMoney(value float64) float64 {
return money.Round(value)
}
func depositFreeQuotaAmountCent(req DepositFreeQuotaRequest) int64 {
if req.AmountCent != 0 {
return req.AmountCent
}
return int64(math.Round(req.Amount * 100))
return req.AmountCent
}
func appendAuditLog(tx *gorm.DB, actorID uint64, action string, bizID uint64, meta AuditMeta, detail map[string]any) error {
@@ -9,18 +9,13 @@ func TestDepositFreeQuotaAmountCent(t *testing.T) {
want int64
}{
{
name: "优先使用分字段",
req: DepositFreeQuotaRequest{AmountCent: 1234, Amount: 99},
want: 1234,
},
{
name: "缺少分字段时回退元字段",
req: DepositFreeQuotaRequest{Amount: 12.34},
name: "使用分字段",
req: DepositFreeQuotaRequest{AmountCent: 1234},
want: 1234,
},
{
name: "负分拒绝",
req: DepositFreeQuotaRequest{AmountCent: -1, Amount: 12.34},
req: DepositFreeQuotaRequest{AmountCent: -1},
want: -1,
},
}
@@ -46,7 +46,7 @@ func (s *Service) SetDepositFreeQuota(adminID uint64, userID uint64, req Deposit
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if userID == 0 || req.Amount < 0 {
if userID == 0 || req.AmountCent < 0 {
return nil, ErrInvalidUser
}
return s.repo.SetDepositFreeQuota(adminID, userID, req, meta)