Files
kra-new/internal/data/asset/asset.go
T
2026-09-23 17:42:55 +08:00

145 lines
5.8 KiB
Go

package asset
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
biz "kra/internal/biz/asset"
"kra/internal/data/ledger"
"time"
)
type Provider interface{ DB() *gorm.DB }
type repo struct{ data Provider }
func NewRepo(data Provider) biz.Repo { return &repo{data: data} }
func (r *repo) db(ctx context.Context) *gorm.DB { return r.data.DB().WithContext(ctx) }
type row struct {
UserID uint `gorm:"column:user_id"`
Username string
Nickname string
Avatar string
Enable int
CreatedAt time.Time
DiamondBalance, TotalRechargeCents, TotalSpentDiamonds int64
LastLoginAt *time.Time
}
func query(db *gorm.DB, f biz.Filter) *gorm.DB {
q := db.Table("sys_users").Joins("LEFT JOIN live_wallets ON live_wallets.user_id = sys_users.id").Where("sys_users.deleted_at IS NULL")
if f.Keyword != "" {
v := "%" + f.Keyword + "%"
q = q.Where("sys_users.username LIKE ? OR sys_users.nick_name LIKE ?", v, v)
}
if f.UserID > 0 {
q = q.Where("sys_users.id = ?", f.UserID)
}
if f.Enabled != nil {
if *f.Enabled {
q = q.Where("sys_users.enable = 1")
} else {
q = q.Where("sys_users.enable <> 1")
}
}
if len(f.CreatedAt) == 2 {
q = q.Where("sys_users.created_at BETWEEN ? AND ?", f.CreatedAt[0], f.CreatedAt[1])
}
return q
}
func (r *repo) List(ctx context.Context, f biz.Filter, o biz.ListOptions) ([]*biz.User, int64, error) {
q := query(r.db(ctx), f)
var total int64
if err := q.Count(&total).Error; err != nil {
return nil, 0, err
}
var rows []row
sel := "sys_users.id AS user_id,sys_users.username,sys_users.nick_name AS nickname,sys_users.header_img AS avatar,sys_users.enable,sys_users.created_at,COALESCE(live_wallets.diamond_balance,0) AS diamond_balance,COALESCE(live_wallets.total_recharge_cents,0) AS total_recharge_cents,COALESCE(live_wallets.total_spent_diamonds,0) AS total_spent_diamonds,(SELECT MAX(created_at) FROM sys_login_logs WHERE user_id=sys_users.id AND status=?) AS last_login_at"
if err := q.Select(sel, true).Order(userOrder(o.OrderBy)).Offset(o.Offset).Limit(o.Limit).Scan(&rows).Error; err != nil {
return nil, 0, err
}
out := make([]*biz.User, 0, len(rows))
for i := range rows {
out = append(out, toBiz(&rows[i]))
}
return out, total, nil
}
func (r *repo) Get(ctx context.Context, id uint) (*biz.User, error) {
items, _, err := r.List(ctx, biz.Filter{UserID: id}, biz.ListOptions{Limit: 1})
if err != nil {
return nil, err
}
if len(items) == 0 {
return nil, biz.ErrNotFound
}
v := items[0]
var orders []ledger.RechargeOrderPO
if err := r.db(ctx).Where("user_id = ?", id).Order("created_at DESC").Limit(20).Find(&orders).Error; err != nil {
return nil, err
}
for _, x := range orders {
status := "pending"
if x.CreditedAt != nil {
status = "paid"
}
v.RecentOrders = append(v.RecentOrders, biz.OrderRecord{OrderNo: x.OrderNo, Diamonds: x.Diamonds, AmountCents: x.AmountCents, Status: status, CreatedAt: x.CreatedAt})
}
var sends []ledger.GiftSendPO
if err := r.db(ctx).Where("user_id = ?", id).Order("created_at DESC").Limit(20).Find(&sends).Error; err != nil {
return nil, err
}
for _, x := range sends {
v.RecentSends = append(v.RecentSends, biz.SendRecord{SerialNo: x.SerialNo, RoomName: x.RoomName, GiftName: x.GiftName, GiftIcon: x.GiftIcon, DiamondTotal: x.DiamondTotal, CreatedAt: x.CreatedAt})
}
return v, nil
}
func (r *repo) SetEnabled(ctx context.Context, id uint, enabled bool) error {
value := 2
if enabled {
value = 1
}
res := r.db(ctx).Table("sys_users").Where("id = ? AND deleted_at IS NULL", id).Update("enable", value)
if res.Error != nil {
return res.Error
}
if res.RowsAffected == 0 {
return biz.ErrNotFound
}
return nil
}
func (r *repo) Adjust(ctx context.Context, v *biz.Adjustment) (*biz.User, error) {
err := r.db(ctx).Transaction(func(tx *gorm.DB) error {
wallet := ledger.WalletPO{UserID: v.UserID}
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("user_id = ?", v.UserID).FirstOrCreate(&wallet).Error; err != nil {
return err
}
balance := wallet.DiamondBalance
if v.Type == "decrease" {
if balance < v.Amount {
return biz.ErrInsufficient
}
balance -= v.Amount
} else {
balance += v.Amount
}
if err := tx.Model(&wallet).Update("diamond_balance", balance).Error; err != nil {
return err
}
return tx.Create(&ledger.BalanceAdjustmentPO{UserID: v.UserID, OperatorUserID: v.OperatorUserID, Type: v.Type, Amount: v.Amount, Reason: v.Reason, BalanceAfter: balance}).Error
})
if err != nil {
return nil, err
}
return r.Get(ctx, v.UserID)
}
func toBiz(v *row) *biz.User {
return &biz.User{UserID: v.UserID, Username: v.Username, Nickname: v.Nickname, Avatar: v.Avatar, Enabled: v.Enable == 1, DiamondBalance: v.DiamondBalance, TotalRechargeCents: v.TotalRechargeCents, TotalSpentDiamonds: v.TotalSpentDiamonds, CreatedAt: v.CreatedAt, LastLoginAt: v.LastLoginAt}
}
func userOrder(v string) string {
m := map[string]string{"id_asc": "sys_users.id ASC", "id_desc": "sys_users.id DESC", "balance_asc": "diamond_balance ASC", "balance_desc": "diamond_balance DESC", "recharge_asc": "total_recharge_cents ASC", "recharge_desc": "total_recharge_cents DESC", "spent_asc": "total_spent_diamonds ASC", "spent_desc": "total_spent_diamonds DESC", "created_asc": "sys_users.created_at ASC", "created_desc": "sys_users.created_at DESC", "created_at_asc": "sys_users.created_at ASC", "created_at_desc": "sys_users.created_at DESC", "last_login_asc": "last_login_at ASC", "last_login_desc": "last_login_at DESC"}
if x := m[v]; x != "" {
return x
}
return "sys_users.id ASC"
}