92 lines
2.8 KiB
Go
92 lines
2.8 KiB
Go
// Package service 沉淀跨接口复用的业务逻辑:积分变动、站点配置读写
|
||
package service
|
||
|
||
import (
|
||
"errors"
|
||
"strconv"
|
||
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/clause"
|
||
|
||
"nl-game-api-gin/internal/database"
|
||
"nl-game-api-gin/internal/model"
|
||
)
|
||
|
||
// ChangePoints 变动用户积分并写流水(事务保证,正数=增加、负数=扣减)
|
||
// tx 传 nil 时自动开启新事务;传入已有事务则复用(用于下单等复合操作)
|
||
func ChangePoints(tx *gorm.DB, userID, change, ptype, relatedID int, remark string) (int, error) {
|
||
run := func(db *gorm.DB) (int, error) {
|
||
var user model.User
|
||
// 行级锁读取(SELECT ... FOR UPDATE),防止并发变动基于同一余额快照造成丢失更新
|
||
// 注意:GORM v2 必须用 clause.Locking,旧的 Set("gorm:query_option") 会被静默忽略
|
||
if err := db.Clauses(clause.Locking{Strength: "UPDATE"}).First(&user, userID).Error; err != nil {
|
||
return 0, errors.New("用户不存在")
|
||
}
|
||
newBalance := user.Points + change
|
||
if newBalance < 0 {
|
||
return 0, errors.New("积分不足")
|
||
}
|
||
// 更新余额;增加时同步累计 total_points(排行榜依据)
|
||
updates := map[string]any{"points": newBalance}
|
||
if change > 0 {
|
||
updates["total_points"] = user.TotalPoints + change
|
||
}
|
||
if err := db.Model(&model.User{}).Where("id = ?", userID).Updates(updates).Error; err != nil {
|
||
return 0, err
|
||
}
|
||
// 写积分流水(审计与个人中心展示)
|
||
record := model.PointRecord{
|
||
UserID: userID, ChangePoints: change, Balance: newBalance,
|
||
Type: ptype, RelatedID: relatedID, Remark: remark,
|
||
}
|
||
if err := db.Create(&record).Error; err != nil {
|
||
return 0, err
|
||
}
|
||
return newBalance, nil
|
||
}
|
||
// 复用外部事务或自行开启事务
|
||
if tx != nil {
|
||
return run(tx)
|
||
}
|
||
var balance int
|
||
err := database.DB.Transaction(func(db *gorm.DB) error {
|
||
b, err := run(db)
|
||
balance = b
|
||
return err
|
||
})
|
||
return balance, err
|
||
}
|
||
|
||
// GetConfig 读取站点配置,不存在时返回默认值
|
||
func GetConfig(key, def string) string {
|
||
var conf model.SiteConfig
|
||
if err := database.DB.Where("config_key = ?", key).First(&conf).Error; err != nil {
|
||
return def
|
||
}
|
||
return conf.ConfigValue
|
||
}
|
||
|
||
// GetConfigInt 读取整数类型站点配置,解析失败返回默认值
|
||
func GetConfigInt(key string, def int) int {
|
||
v := GetConfig(key, "")
|
||
if v == "" {
|
||
return def
|
||
}
|
||
n, err := strconv.Atoi(v)
|
||
if err != nil {
|
||
return def
|
||
}
|
||
return n
|
||
}
|
||
|
||
// SetConfig 写入站点配置(不存在则创建)
|
||
func SetConfig(key, value string) error {
|
||
var conf model.SiteConfig
|
||
err := database.DB.Where("config_key = ?", key).First(&conf).Error
|
||
if err != nil {
|
||
conf = model.SiteConfig{ConfigKey: key, ConfigValue: value}
|
||
return database.DB.Create(&conf).Error
|
||
}
|
||
return database.DB.Model(&conf).Update("config_value", value).Error
|
||
}
|