feat: 赋能溢出截断 + 前端按已赋能量禁用按钮
- 单篇赋能上限由硬拒绝改为溢出截断,类似每日经验处理 - 前端弹窗时查询文章已赋能总量,>=10 禁用重赋,>=20 全部禁用 - GetEnergyInfo 支持 ?post_id= 返回 post_energize_total - 文案精简为'赋能成功!+N 经验'等短句
This commit is contained in:
@ -37,7 +37,7 @@ func (ec *EnergyController) Energize(c *gin.Context) {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case common.ErrPostNotFound:
|
case common.ErrPostNotFound:
|
||||||
@ -56,21 +56,30 @@ func (ec *EnergyController) Energize(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
truncated := actualAmount != req.Amount
|
||||||
common.Ok(c, gin.H{
|
common.Ok(c, gin.H{
|
||||||
"exp_gained": expGained,
|
"exp_gained": expGained,
|
||||||
"message": buildEnergizeMessage(expGained),
|
"actual_amount": actualAmount,
|
||||||
|
"truncated": truncated,
|
||||||
|
"message": buildEnergizeMessage(expGained, truncated),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildEnergizeMessage 根据获得的经验生成提示消息
|
// buildEnergizeMessage 根据获得的经验生成提示消息
|
||||||
func buildEnergizeMessage(expGained int) string {
|
func buildEnergizeMessage(expGained int, truncated bool) string {
|
||||||
|
if truncated {
|
||||||
if expGained > 0 {
|
if expGained > 0 {
|
||||||
return "赋能成功!" + strconv.Itoa(expGained) + "经验已加入你的旅程。"
|
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) {
|
func (ec *EnergyController) GetEnergyInfo(c *gin.Context) {
|
||||||
uid, _, ok := common.GetGinUser(c)
|
uid, _, ok := common.GetGinUser(c)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -78,7 +87,10 @@ func (ec *EnergyController) GetEnergyInfo(c *gin.Context) {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
common.Error(c, http.StatusInternalServerError, "获取域能信息失败")
|
common.Error(c, http.StatusInternalServerError, "获取域能信息失败")
|
||||||
return
|
return
|
||||||
|
|||||||
@ -85,8 +85,8 @@ type levelUseCase interface {
|
|||||||
|
|
||||||
// energyUseCase EnergyController 对 EnergyService 的最小依赖(ISP:3 个方法)
|
// energyUseCase EnergyController 对 EnergyService 的最小依赖(ISP:3 个方法)
|
||||||
type energyUseCase interface {
|
type energyUseCase interface {
|
||||||
Energize(energizerID uint, postID uint, amount int) (int, error)
|
Energize(energizerID uint, postID uint, amount int) (int, int, error)
|
||||||
GetEnergyInfo(userID uint) (*service.EnergyInfo, error)
|
GetEnergyInfo(userID uint, postID uint) (*service.EnergyInfo, error)
|
||||||
GetEnergyLogs(userID uint, days, page, pageSize int) ([]model.EnergyLog, int64, error)
|
GetEnergyLogs(userID uint, days, page, pageSize int) ([]model.EnergyLog, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Energize 赋能文章
|
// Energize 赋能文章
|
||||||
// 返回:获得的经验值、错误
|
// 返回:获得的经验值、实际赋能量(DB乘10)、错误
|
||||||
func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int, error) {
|
func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int, int, error) {
|
||||||
// 验证赋能量
|
// 验证赋能量
|
||||||
if amount != EnergyEnergize1 && amount != EnergyEnergize2 {
|
if amount != EnergyEnergize1 && amount != EnergyEnergize2 {
|
||||||
return 0, common.ErrInvalidEnergyAmount
|
return 0, 0, common.ErrInvalidEnergyAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
var expGained int
|
var expGained int
|
||||||
@ -21,6 +21,9 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
|||||||
var expToday time.Time
|
var expToday time.Time
|
||||||
var expTotal int
|
var expTotal int
|
||||||
|
|
||||||
|
// 实际使用的赋能量(可能因上限被截断)
|
||||||
|
actualAmount := amount
|
||||||
|
|
||||||
err := s.repo.Transaction(func(txRepo EnergyStore, txFundRepo FundStore) error {
|
err := s.repo.Transaction(func(txRepo EnergyStore, txFundRepo FundStore) error {
|
||||||
// 1. 检查帖子存在,获取作者
|
// 1. 检查帖子存在,获取作者
|
||||||
post, err := txRepo.FindPostByID(postID)
|
post, err := txRepo.FindPostByID(postID)
|
||||||
@ -42,14 +45,21 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
|||||||
return common.ErrInsufficientEnergy
|
return common.ErrInsufficientEnergy
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 检查单用户单文章赋能总量 ≤ MaxEnergizePerPost
|
// 4. 检查单用户单文章赋能上限,超出部分截断(类似每日经验溢出截断)
|
||||||
total, err := txRepo.SumUserPostEnergy(energizerID, postID)
|
total, err := txRepo.SumUserPostEnergy(energizerID, postID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("查询赋能记录失败: %w", err)
|
return fmt.Errorf("查询赋能记录失败: %w", err)
|
||||||
}
|
}
|
||||||
if total+amount > MaxEnergizePerPost {
|
room := MaxEnergizePerPost - total
|
||||||
|
if room <= 0 {
|
||||||
return common.ErrEnergizeLimitReached
|
return common.ErrEnergizeLimitReached
|
||||||
}
|
}
|
||||||
|
if amount > room {
|
||||||
|
actualAmount = room // 截断到剩余空间
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新赋值 amount 用于后续扣域能、写日志等
|
||||||
|
amount = actualAmount
|
||||||
|
|
||||||
// 5. 扣赋能者域能
|
// 5. 扣赋能者域能
|
||||||
if err := txRepo.AddEnergy(energizerID, -amount); err != nil {
|
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
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 12. 事务提交后,在事务外增加经验 + 更新每日汇总
|
// 12. 事务提交后,在事务外增加经验 + 更新每日汇总
|
||||||
if needExpUpdate {
|
if needExpUpdate {
|
||||||
if s.expSvc != nil {
|
if s.expSvc != nil {
|
||||||
if _, _, err := s.expSvc.AddExp(energizerID, expGained); err != 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{
|
newSummary := &model.DailyExpSummary{
|
||||||
@ -171,9 +181,9 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
|||||||
ExpEarned: expTotal,
|
ExpEarned: expTotal,
|
||||||
}
|
}
|
||||||
if err := s.repo.UpsertDailyExp(newSummary); err != nil {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetEnergyInfo 获取用户域能余额和每日赋能经验状态
|
// 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)
|
user, err := s.repo.FindUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -24,12 +25,21 @@ func (s *EnergyService) GetEnergyInfo(userID uint) (*EnergyInfo, error) {
|
|||||||
dailyExp = summary.ExpEarned
|
dailyExp = summary.ExpEarned
|
||||||
}
|
}
|
||||||
|
|
||||||
return &EnergyInfo{
|
info := &EnergyInfo{
|
||||||
Energy: user.Energy,
|
Energy: user.Energy,
|
||||||
DailyEnergizeExp: dailyExp,
|
DailyEnergizeExp: dailyExp,
|
||||||
DailyExpCap: DailyEnergizeExpCap,
|
DailyExpCap: DailyEnergizeExpCap,
|
||||||
DailyExpCapReached: dailyExp >= 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 天)
|
// GetEnergyLogs 分页查询用户域能流水(近 N 天)
|
||||||
|
|||||||
@ -34,6 +34,7 @@ type EnergyInfo struct {
|
|||||||
DailyEnergizeExp int // 当日已通过赋能获得的经验值
|
DailyEnergizeExp int // 当日已通过赋能获得的经验值
|
||||||
DailyExpCap int // 每日赋能经验上限
|
DailyExpCap int // 每日赋能经验上限
|
||||||
DailyExpCapReached bool // 是否已达上限
|
DailyExpCapReached bool // 是否已达上限
|
||||||
|
PostEnergizeTotal int // 当前文章已赋能量(DB乘10,仅在传 postID 时返回)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 域能相关常量(乘10存储,DB值)
|
// 域能相关常量(乘10存储,DB值)
|
||||||
|
|||||||
@ -32,6 +32,25 @@
|
|||||||
capHint.style.display = 'none';
|
capHint.style.display = 'none';
|
||||||
lightBtn.disabled = false;
|
lightBtn.disabled = false;
|
||||||
heavyBtn.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() {
|
btn.addEventListener('click', function() {
|
||||||
@ -39,12 +58,20 @@
|
|||||||
showToast('无法给自己赋能');
|
showToast('无法给自己赋能');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetch('/api/energy/info')
|
fetch('/api/energy/info?post_id=' + postId)
|
||||||
.then(function(r) { return r.json(); })
|
.then(function(r) { return r.json(); })
|
||||||
.then(function(d) {
|
.then(function(d) {
|
||||||
if (d.success && d.data && d.data.daily_exp_cap_reached) {
|
if (d.success && d.data) {
|
||||||
|
if (d.data.daily_exp_cap_reached) {
|
||||||
capHint.style.display = 'block';
|
capHint.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
var total = d.data.post_energize_total || 0;
|
||||||
|
if (total >= 20) {
|
||||||
|
disableAll();
|
||||||
|
} else if (total >= 10) {
|
||||||
|
disableHeavy();
|
||||||
|
}
|
||||||
|
}
|
||||||
showOverlay();
|
showOverlay();
|
||||||
})
|
})
|
||||||
.catch(function() { showOverlay(); });
|
.catch(function() { showOverlay(); });
|
||||||
|
|||||||
Reference in New Issue
Block a user