fix: 域能系统全量检查 — 修复 5 个逻辑/交互 Gap
- 修复 Admin 改名路径未扣域能(非审核路径补充 HasCompletedTask + DeductOnRename) - 赋能按钮改为 Modal 弹窗(轻赋/重赋选择 + 每日上限提示) - EnergyInfo API 增加 daily_energize_exp/daily_exp_cap_reached 返回值 - "我的域能" TAB 增加经验值和等级展示 - Energize 事务修复:导出 EnergyStore + WithTx 方法确保 DB 操作原子性 - Studio 写文章页新增 LV0 锁止横幅 + 禁用提交按钮
This commit is contained in:
@ -17,17 +17,25 @@ type expAdder interface {
|
||||
|
||||
// EnergyService 域能业务逻辑
|
||||
type EnergyService struct {
|
||||
db *gorm.DB
|
||||
repo energyStore
|
||||
expSvc expAdder
|
||||
db *gorm.DB
|
||||
repo EnergyStore
|
||||
expSvc expAdder
|
||||
notifSvc energyNotifier
|
||||
}
|
||||
|
||||
// NewEnergyService 构造函数
|
||||
func NewEnergyService(db *gorm.DB, repo energyStore, expSvc expAdder, notifSvc energyNotifier) *EnergyService {
|
||||
func NewEnergyService(db *gorm.DB, repo EnergyStore, expSvc expAdder, notifSvc energyNotifier) *EnergyService {
|
||||
return &EnergyService{db: db, repo: repo, expSvc: expSvc, notifSvc: notifSvc}
|
||||
}
|
||||
|
||||
// EnergyInfo 域能信息(含每日赋能经验状态)
|
||||
type EnergyInfo struct {
|
||||
Energy int // 域能余额(乘10存储)
|
||||
DailyEnergizeExp int // 当日已通过赋能获得的经验值
|
||||
DailyExpCap int // 每日赋能经验上限
|
||||
DailyExpCapReached bool // 是否已达上限
|
||||
}
|
||||
|
||||
// SetExpAdder 链式注入经验服务(用于赋能获得经验)
|
||||
func (s *EnergyService) SetExpAdder(svc expAdder) {
|
||||
s.expSvc = svc
|
||||
@ -57,8 +65,10 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
var expGained int
|
||||
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
txRepo := s.repo.WithTx(tx)
|
||||
|
||||
// 1. 检查帖子存在,获取作者
|
||||
post, err := s.repo.FindPostByID(postID)
|
||||
post, err := txRepo.FindPostByID(postID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return common.ErrPostNotFound
|
||||
@ -72,7 +82,7 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
}
|
||||
|
||||
// 3. 检查赋能者域能余额(owner 跳过)
|
||||
energizer, err := s.repo.FindUserByID(energizerID)
|
||||
energizer, err := txRepo.FindUserByID(energizerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询用户失败: %w", err)
|
||||
}
|
||||
@ -81,7 +91,7 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
}
|
||||
|
||||
// 4. 检查单用户单文章赋能总量 ≤ MaxEnergizePerPost
|
||||
total, err := s.repo.SumUserPostEnergy(energizerID, postID)
|
||||
total, err := txRepo.SumUserPostEnergy(energizerID, postID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询赋能记录失败: %w", err)
|
||||
}
|
||||
@ -90,18 +100,18 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
}
|
||||
|
||||
// 5. 扣赋能者域能
|
||||
if err := s.repo.AddEnergy(energizerID, -amount); err != nil {
|
||||
if err := txRepo.AddEnergy(energizerID, -amount); err != nil {
|
||||
return fmt.Errorf("扣减域能失败: %w", err)
|
||||
}
|
||||
|
||||
// 6. 增加被赋能者(文章作者)域能(赋能量的十分之一)
|
||||
receivedAmount := amount / 10 // 1 域能→0.1,2 域能→0.2
|
||||
if err := s.repo.AddEnergy(post.UserID, receivedAmount); err != nil {
|
||||
if err := txRepo.AddEnergy(post.UserID, receivedAmount); err != nil {
|
||||
return fmt.Errorf("增加被赋能者域能失败: %w", err)
|
||||
}
|
||||
|
||||
// 7. 更新 Post.total_energy_received
|
||||
if err := s.repo.IncrPostEnergy(postID, amount); err != nil {
|
||||
if err := txRepo.IncrPostEnergy(postID, amount); err != nil {
|
||||
return fmt.Errorf("更新文章赋能计数失败: %w", err)
|
||||
}
|
||||
|
||||
@ -111,7 +121,7 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
PostID: postID,
|
||||
Amount: amount,
|
||||
}
|
||||
if err := s.repo.CreateEnergizeLog(eLog); err != nil {
|
||||
if err := txRepo.CreateEnergizeLog(eLog); err != nil {
|
||||
return fmt.Errorf("创建赋能记录失败: %w", err)
|
||||
}
|
||||
|
||||
@ -127,7 +137,7 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
RelatedID: postID,
|
||||
Description: fmt.Sprintf("给文章 %d 赋能 %.1f 域能", postID, amountDisplay),
|
||||
}
|
||||
if err := s.repo.CreateEnergyLog(energizerLog); err != nil {
|
||||
if err := txRepo.CreateEnergyLog(energizerLog); err != nil {
|
||||
return fmt.Errorf("创建赋能者流水失败: %w", err)
|
||||
}
|
||||
|
||||
@ -139,7 +149,7 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
RelatedID: postID,
|
||||
Description: fmt.Sprintf("文章 %d 被赋能 +%.1f 域能", postID, receivedDisplay),
|
||||
}
|
||||
if err := s.repo.CreateEnergyLog(energizedLog); err != nil {
|
||||
if err := txRepo.CreateEnergyLog(energizedLog); err != nil {
|
||||
return fmt.Errorf("创建被赋能者流水失败: %w", err)
|
||||
}
|
||||
|
||||
@ -148,7 +158,7 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
now := time.Now().In(loc)
|
||||
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
||||
|
||||
summary, err := s.repo.GetDailyEnergizeExp(energizerID, today)
|
||||
summary, err := txRepo.GetDailyEnergizeExp(energizerID, today)
|
||||
currentDaily := 0
|
||||
if err == nil {
|
||||
currentDaily = summary.ExpEarned
|
||||
@ -177,7 +187,7 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
||||
Date: today,
|
||||
ExpEarned: currentDaily + expGain,
|
||||
}
|
||||
if err := s.repo.UpsertDailyExp(newSummary); err != nil {
|
||||
if err := txRepo.UpsertDailyExp(newSummary); err != nil {
|
||||
return fmt.Errorf("更新每日经验汇总失败: %w", err)
|
||||
}
|
||||
}
|
||||
@ -282,13 +292,29 @@ func (s *EnergyService) AdminAdjust(operatorUID uint, userIDs []uint, amount int
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetEnergyInfo 获取用户域能余额
|
||||
func (s *EnergyService) GetEnergyInfo(userID uint) (int, error) {
|
||||
// GetEnergyInfo 获取用户域能余额和每日赋能经验状态
|
||||
func (s *EnergyService) GetEnergyInfo(userID uint) (*EnergyInfo, error) {
|
||||
user, err := s.repo.FindUserByID(userID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return nil, err
|
||||
}
|
||||
return user.Energy, nil
|
||||
|
||||
loc, _ := time.LoadLocation("Asia/Shanghai")
|
||||
now := time.Now().In(loc)
|
||||
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
||||
|
||||
dailyExp := 0
|
||||
summary, err := s.repo.GetDailyEnergizeExp(userID, today)
|
||||
if err == nil {
|
||||
dailyExp = summary.ExpEarned
|
||||
}
|
||||
|
||||
return &EnergyInfo{
|
||||
Energy: user.Energy,
|
||||
DailyEnergizeExp: dailyExp,
|
||||
DailyExpCap: DailyEnergizeExpCap,
|
||||
DailyExpCapReached: dailyExp >= DailyEnergizeExpCap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetEnergyLogs 分页查询用户域能流水(近 N 天)
|
||||
|
||||
@ -4,6 +4,8 @@ import (
|
||||
"time"
|
||||
|
||||
"metazone.cc/metalab/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// userAuthStore AuthService 所需的最小仓储接口(ISP:7 个方法)
|
||||
@ -87,8 +89,8 @@ type levelNotifier interface {
|
||||
Create(userID uint, notifyType, title, content string, relatedID *uint) error
|
||||
}
|
||||
|
||||
// energyStore EnergyService 所需的完整仓储接口
|
||||
type energyStore interface {
|
||||
// EnergyStore EnergyService 所需的完整仓储接口
|
||||
type EnergyStore interface {
|
||||
FindUserByID(userID uint) (*model.User, error)
|
||||
FindPostByID(postID uint) (*model.Post, error)
|
||||
AddEnergy(userID uint, amount int) error
|
||||
@ -102,8 +104,13 @@ type energyStore interface {
|
||||
ListEnergyLogsByUser(userID uint, days int, offset, limit int) ([]model.EnergyLog, error)
|
||||
CountEnergyLogsByUser(userID uint, days int) (int64, error)
|
||||
ListAllEnergyLogs(energyType string, offset, limit int) ([]model.EnergyLog, int64, error)
|
||||
// WithTx 基于给定事务连接创建新的 EnergyStore(确保事务内操作原子性)
|
||||
WithTx(tx *gorm.DB) EnergyStore
|
||||
}
|
||||
|
||||
// energyStore 向后兼容别名
|
||||
type energyStore = EnergyStore
|
||||
|
||||
// energyNotifier EnergyService 所需的通知接口(ISP)
|
||||
type energyNotifier interface {
|
||||
Create(userID uint, notifyType, title, content string, relatedID *uint) error
|
||||
|
||||
Reference in New Issue
Block a user