Files
mce/internal/service/level_service.go
Victor_Jay 97adf54d6d fix: golangci-lint 零告警通过 (57→0) + gofumpt/goimports 全量格式化
## CI 修复 (P0/P1)

P0 — 编译阻塞:
  - interfaces.go: postUseCase ISP 接口移除不用的 Create/Update/Delete

P1 — 必须修复:
  - ST1000: 为 13 个包添加包注释 (common/config/model/service/...)
  - ST1005: redis_store.go 全部错误消息改为小写开头
  - errcheck (19处): defer Close()→闭包忽略, notifier.Create→_=, r.Run→检查error
  - errorlint (9处): switch-on-error→errors.Is 链, ==→errors.Is
  - ST1020/ST1022: 导出符号注释以符号名开头

P2 — 安全评审:
  - gosec (12处): G203/G301/G304/G306 添加 nolint 注释并附理由

P3 — 清理:
  - unused: 移除 hasUnicode/energyStore/current/energyAdminUseCase
  - gofumpt + goimports 全量格式化 (35+ 文件)
2026-06-22 02:27:35 +08:00

174 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"fmt"
"time"
"metazone.cc/mce/internal/model"
)
// LevelService 积分与等级业务逻辑
type LevelService struct {
userRepo userLevelStore
checkRepo checkInStore
taskRepo taskStore
notifSvc levelNotifier
energyCheckInSvc energyCheckIner
}
// energyCheckIner LevelService 对域能签到的最小依赖ISP
type energyCheckIner interface {
CheckInEnergy(userID uint) error
}
// NewLevelService 构造函数
func NewLevelService(userRepo userLevelStore, checkRepo checkInStore, taskRepo taskStore, notifSvc levelNotifier) *LevelService {
return &LevelService{userRepo: userRepo, checkRepo: checkRepo, taskRepo: taskRepo, notifSvc: notifSvc}
}
// WithEnergyService 链式注入域能服务(用于签到域能奖励)
func (s *LevelService) WithEnergyService(svc energyCheckIner) *LevelService {
s.energyCheckInSvc = svc
return s
}
// GetLevel 获取用户当前等级、经验值和今日签到状态
func (s *LevelService) GetLevel(userID uint) (level int, exp int, checkedIn bool, err error) {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return 0, 0, false, err
}
loc, _ := time.LoadLocation("Asia/Shanghai")
now := time.Now().In(loc)
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
_, err = s.checkRepo.FindCheckIn(userID, today)
checkedIn = err == nil
return model.GetLevelByExp(user.Exp), user.Exp, checkedIn, nil
}
// AddExp 增加经验值,自动检查升级并发送通知
// 返回:新经验值、是否升级、错误
func (s *LevelService) AddExp(userID uint, delta int) (newExp int, levelUp bool, err error) {
if delta <= 0 {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return 0, false, err
}
return user.Exp, false, nil
}
// 先查旧等级
user, err := s.userRepo.FindByID(userID)
if err != nil {
return 0, false, err
}
oldLevel := model.GetLevelByExp(user.Exp)
// 原子增加经验值
if err := s.userRepo.AddExp(userID, delta); err != nil {
return 0, false, err
}
// 查新等级
user, err = s.userRepo.FindByID(userID)
if err != nil {
return 0, false, err
}
newLevel := model.GetLevelByExp(user.Exp)
if newLevel > oldLevel {
levelUp = true
if s.notifSvc != nil {
_ = s.notifSvc.Create(userID, model.NotifyLevelUp,
"等级提升",
fmt.Sprintf("恭喜您!您的等级已提升至 %s", model.GetLevelName(newLevel)),
nil, nil)
}
}
return user.Exp, levelUp, nil
}
// CheckIn 执行签到(基于 Asia/Shanghai 自然日严格防重)
// 返回:是否成功签到、新经验值、是否升级、错误
// LV0 用户不触发签到
func (s *LevelService) CheckIn(userID uint) (checkedIn bool, newExp int, levelUp bool, err error) {
// LV0 用户不触发签到
user, err := s.userRepo.FindByID(userID)
if err != nil {
return false, 0, false, err
}
if user.Exp < 1 {
return false, user.Exp, false, 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)
// 检查今日是否已签到
_, err = s.checkRepo.FindCheckIn(userID, today)
if err == nil {
// 已签到
return false, user.Exp, false, nil
}
// 创建签到记录
ci := &model.UserCheckIn{
UserID: userID,
Date: today,
}
if err := s.checkRepo.CreateCheckIn(ci); err != nil {
// 唯一索引冲突 → 已签到
return false, user.Exp, false, nil
}
// 增加域能(静默失败不影响签到经验)
if s.energyCheckInSvc != nil {
_ = s.energyCheckInSvc.CheckInEnergy(userID)
}
// 增加经验值
newExp, levelUp, err = s.AddExp(userID, 5)
return true, newExp, levelUp, err
}
// HasCompletedTask 检查用户是否已完成过指定任务(无副作用查询)
func (s *LevelService) HasCompletedTask(userID uint, taskType string) (bool, error) {
return s.taskRepo.HasTask(userID, taskType)
}
// CompleteTask 完成首次任务(严格防重)
// 返回:新经验值、是否升级、错误
func (s *LevelService) CompleteTask(userID uint, taskType string) (newExp int, levelUp bool, err error) {
// 检查是否已完成
_, err = s.taskRepo.FindTask(userID, taskType)
if err == nil {
// 已完成过,不重复奖励
user, _ := s.userRepo.FindByID(userID)
if user != nil {
return user.Exp, false, nil
}
return 0, false, nil
}
// 创建任务记录
task := &model.UserTask{
UserID: userID,
TaskType: taskType,
}
if err := s.taskRepo.CreateTask(task); err != nil {
// 唯一索引冲突 → 已完成
user, _ := s.userRepo.FindByID(userID)
if user != nil {
return user.Exp, false, nil
}
return 0, false, nil
}
// 增加经验值
return s.AddExp(userID, 10)
}