diff --git a/internal/controller/energy_controller.go b/internal/controller/energy_controller.go index 688b9ec..2bbb474 100644 --- a/internal/controller/energy_controller.go +++ b/internal/controller/energy_controller.go @@ -37,7 +37,7 @@ func (ec *EnergyController) Energize(c *gin.Context) { return } - expGained, err := ec.energySvc.Energize(uid, req.PostID, req.Amount) + expGained, actualAmount, err := ec.energySvc.Energize(uid, req.PostID, req.Amount) if err != nil { switch err { case common.ErrPostNotFound: @@ -56,21 +56,30 @@ func (ec *EnergyController) Energize(c *gin.Context) { return } + truncated := actualAmount != req.Amount common.Ok(c, gin.H{ - "exp_gained": expGained, - "message": buildEnergizeMessage(expGained), + "exp_gained": expGained, + "actual_amount": actualAmount, + "truncated": truncated, + "message": buildEnergizeMessage(expGained, truncated), }) } // buildEnergizeMessage 根据获得的经验生成提示消息 -func buildEnergizeMessage(expGained int) string { - if expGained > 0 { - return "赋能成功!" + strconv.Itoa(expGained) + "经验已加入你的旅程。" +func buildEnergizeMessage(expGained int, truncated bool) string { + if truncated { + if expGained > 0 { + return "单篇额度已满,实际赋能部分额度,+" + strconv.Itoa(expGained) + " 经验" + } + return "单篇额度已满,实际赋能部分额度" } - return "赋能成功!今日赋能经验奖励已达上限,对方仍会获得激励。" + if expGained > 0 { + return "赋能成功!+" + strconv.Itoa(expGained) + " 经验" + } + return "赋能成功!今日经验已满,对方仍获得激励" } -// GetEnergyInfo 获取域能余额和经验上限状态 +// GetEnergyInfo 获取域能余额和经验上限状态,支持 ?post_id= 查询文章赋能总量 func (ec *EnergyController) GetEnergyInfo(c *gin.Context) { uid, _, ok := common.GetGinUser(c) if !ok { @@ -78,7 +87,10 @@ func (ec *EnergyController) GetEnergyInfo(c *gin.Context) { return } - info, err := ec.energySvc.GetEnergyInfo(uid) + postIDStr := c.DefaultQuery("post_id", "0") + postID, _ := strconv.ParseUint(postIDStr, 10, 64) + + info, err := ec.energySvc.GetEnergyInfo(uid, uint(postID)) if err != nil { common.Error(c, http.StatusInternalServerError, "获取域能信息失败") return diff --git a/internal/controller/interfaces.go b/internal/controller/interfaces.go index a4be324..2c729dd 100644 --- a/internal/controller/interfaces.go +++ b/internal/controller/interfaces.go @@ -85,8 +85,8 @@ type levelUseCase interface { // energyUseCase EnergyController 对 EnergyService 的最小依赖(ISP:3 个方法) type energyUseCase interface { - Energize(energizerID uint, postID uint, amount int) (int, error) - GetEnergyInfo(userID uint) (*service.EnergyInfo, error) + Energize(energizerID uint, postID uint, amount int) (int, int, error) + GetEnergyInfo(userID uint, postID uint) (*service.EnergyInfo, error) GetEnergyLogs(userID uint, days, page, pageSize int) ([]model.EnergyLog, int64, error) } diff --git a/internal/service/energy_energize.go b/internal/service/energy_energize.go index 19ca1bd..7945f1f 100644 --- a/internal/service/energy_energize.go +++ b/internal/service/energy_energize.go @@ -9,11 +9,11 @@ import ( ) // Energize 赋能文章 -// 返回:获得的经验值、错误 -func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int, error) { +// 返回:获得的经验值、实际赋能量(DB乘10)、错误 +func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int, int, error) { // 验证赋能量 if amount != EnergyEnergize1 && amount != EnergyEnergize2 { - return 0, common.ErrInvalidEnergyAmount + return 0, 0, common.ErrInvalidEnergyAmount } var expGained int @@ -21,6 +21,9 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int var expToday time.Time var expTotal int + // 实际使用的赋能量(可能因上限被截断) + actualAmount := amount + err := s.repo.Transaction(func(txRepo EnergyStore, txFundRepo FundStore) error { // 1. 检查帖子存在,获取作者 post, err := txRepo.FindPostByID(postID) @@ -42,14 +45,21 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int return common.ErrInsufficientEnergy } - // 4. 检查单用户单文章赋能总量 ≤ MaxEnergizePerPost + // 4. 检查单用户单文章赋能上限,超出部分截断(类似每日经验溢出截断) total, err := txRepo.SumUserPostEnergy(energizerID, postID) if err != nil { return fmt.Errorf("查询赋能记录失败: %w", err) } - if total+amount > MaxEnergizePerPost { + room := MaxEnergizePerPost - total + if room <= 0 { return common.ErrEnergizeLimitReached } + if amount > room { + actualAmount = room // 截断到剩余空间 + } + + // 重新赋值 amount 用于后续扣域能、写日志等 + amount = actualAmount // 5. 扣赋能者域能 if err := txRepo.AddEnergy(energizerID, -amount); err != nil { @@ -155,14 +165,14 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int return nil }) if err != nil { - return 0, err + return 0, 0, err } // 12. 事务提交后,在事务外增加经验 + 更新每日汇总 if needExpUpdate { if s.expSvc != nil { if _, _, err := s.expSvc.AddExp(energizerID, expGained); err != nil { - return expGained, fmt.Errorf("赋能成功,但增加经验失败: %w", err) + return expGained, actualAmount, fmt.Errorf("赋能成功,但增加经验失败: %w", err) } } newSummary := &model.DailyExpSummary{ @@ -171,9 +181,9 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int ExpEarned: expTotal, } if err := s.repo.UpsertDailyExp(newSummary); err != nil { - return expGained, fmt.Errorf("赋能成功,但更新经验汇总失败: %w", err) + return expGained, actualAmount, fmt.Errorf("赋能成功,但更新经验汇总失败: %w", err) } } - return expGained, nil + return expGained, actualAmount, nil } diff --git a/internal/service/energy_query.go b/internal/service/energy_query.go index 6ea6768..3e340f0 100644 --- a/internal/service/energy_query.go +++ b/internal/service/energy_query.go @@ -8,7 +8,8 @@ import ( ) // GetEnergyInfo 获取用户域能余额和每日赋能经验状态 -func (s *EnergyService) GetEnergyInfo(userID uint) (*EnergyInfo, error) { +// postID 为 0 时不查文章赋能总量 +func (s *EnergyService) GetEnergyInfo(userID uint, postID uint) (*EnergyInfo, error) { user, err := s.repo.FindUserByID(userID) if err != nil { return nil, err @@ -24,12 +25,21 @@ func (s *EnergyService) GetEnergyInfo(userID uint) (*EnergyInfo, error) { dailyExp = summary.ExpEarned } - return &EnergyInfo{ + info := &EnergyInfo{ Energy: user.Energy, DailyEnergizeExp: dailyExp, DailyExpCap: DailyEnergizeExpCap, DailyExpCapReached: dailyExp >= DailyEnergizeExpCap, - }, nil + } + + if postID != 0 { + total, err := s.repo.SumUserPostEnergy(userID, postID) + if err == nil { + info.PostEnergizeTotal = total + } + } + + return info, nil } // GetEnergyLogs 分页查询用户域能流水(近 N 天) diff --git a/internal/service/energy_service.go b/internal/service/energy_service.go index 29880db..2c2d60e 100644 --- a/internal/service/energy_service.go +++ b/internal/service/energy_service.go @@ -30,10 +30,11 @@ func (s *EnergyService) SetFundRepo(repo FundStore) { // EnergyInfo 域能信息(含每日赋能经验状态) type EnergyInfo struct { - Energy int // 域能余额(乘10存储) - DailyEnergizeExp int // 当日已通过赋能获得的经验值 - DailyExpCap int // 每日赋能经验上限 - DailyExpCapReached bool // 是否已达上限 + Energy int // 域能余额(乘10存储) + DailyEnergizeExp int // 当日已通过赋能获得的经验值 + DailyExpCap int // 每日赋能经验上限 + DailyExpCapReached bool // 是否已达上限 + PostEnergizeTotal int // 当前文章已赋能量(DB乘10,仅在传 postID 时返回) } // 域能相关常量(乘10存储,DB值) diff --git a/templates/MetaLab-2026/static/js/post-energize.js b/templates/MetaLab-2026/static/js/post-energize.js index 0e17b2d..7135d92 100644 --- a/templates/MetaLab-2026/static/js/post-energize.js +++ b/templates/MetaLab-2026/static/js/post-energize.js @@ -32,6 +32,25 @@ capHint.style.display = 'none'; lightBtn.disabled = false; heavyBtn.disabled = false; + heavyBtn.title = ''; + heavyBtn.style.opacity = ''; + lightBtn.title = ''; + lightBtn.style.opacity = ''; + } + + function disableHeavy() { + heavyBtn.disabled = true; + heavyBtn.title = '已达单篇上限(1/2)'; + heavyBtn.style.opacity = '0.4'; + } + + function disableAll() { + lightBtn.disabled = true; + heavyBtn.disabled = true; + lightBtn.title = '已达单篇上限(2/2)'; + lightBtn.style.opacity = '0.4'; + heavyBtn.title = '已达单篇上限(2/2)'; + heavyBtn.style.opacity = '0.4'; } btn.addEventListener('click', function() { @@ -39,11 +58,19 @@ showToast('无法给自己赋能'); return; } - fetch('/api/energy/info') + fetch('/api/energy/info?post_id=' + postId) .then(function(r) { return r.json(); }) .then(function(d) { - if (d.success && d.data && d.data.daily_exp_cap_reached) { - capHint.style.display = 'block'; + if (d.success && d.data) { + if (d.data.daily_exp_cap_reached) { + capHint.style.display = 'block'; + } + var total = d.data.post_energize_total || 0; + if (total >= 20) { + disableAll(); + } else if (total >= 10) { + disableHeavy(); + } } showOverlay(); })