duidui_fiber/internal/camp/service/user_camp_service.go
2026-03-27 10:34:03 +08:00

224 lines
7.2 KiB
Go
Raw Permalink 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"
"dd_fiber_api/internal/camp"
"dd_fiber_api/internal/camp/dao"
question_dao "dd_fiber_api/internal/question/dao"
"dd_fiber_api/pkg/snowflake"
"dd_fiber_api/pkg/utils"
)
// UserCampService 用户打卡营服务
type UserCampService struct {
userCampDAO *dao.UserCampDAO
sectionDAO *dao.SectionDAO
progressDAO *dao.ProgressDAO
taskDAO *dao.TaskDAO
resetHistoryDAO *dao.ResetHistoryDAO
answerRecordDAO question_dao.AnswerRecordDAOInterface
}
// NewUserCampService 创建用户打卡营服务
func NewUserCampService(
userCampDAO *dao.UserCampDAO,
sectionDAO *dao.SectionDAO,
progressDAO *dao.ProgressDAO,
taskDAO *dao.TaskDAO,
resetHistoryDAO *dao.ResetHistoryDAO,
answerRecordDAO question_dao.AnswerRecordDAOInterface,
) *UserCampService {
return &UserCampService{
userCampDAO: userCampDAO,
sectionDAO: sectionDAO,
progressDAO: progressDAO,
taskDAO: taskDAO,
resetHistoryDAO: resetHistoryDAO,
answerRecordDAO: answerRecordDAO,
}
}
// JoinCamp 用户加入打卡营(幂等)
func (s *UserCampService) JoinCamp(req *camp.JoinCampRequest) (*camp.JoinCampResponse, error) {
if req.UserID == "" || req.CampID == "" {
return &camp.JoinCampResponse{Success: false, Message: "参数缺失"}, nil
}
id := snowflake.GetInstance().NextIDString()
if err := s.userCampDAO.CreateIfNotExists(id, req.UserID, req.CampID); err != nil {
return &camp.JoinCampResponse{Success: false, Message: fmt.Sprintf("加入失败: %v", err)}, nil
}
// 获取打卡营中 section_number 最小的小节ID
firstSectionID, err := s.sectionDAO.GetFirstSectionID(req.CampID)
if err != nil {
return &camp.JoinCampResponse{Success: false, Message: fmt.Sprintf("获取第一个小节失败: %v", err)}, nil
}
// 如果找到了第一个小节且当前小节ID为空则设置为第一个小节
if firstSectionID != "" {
currentSectionID, err := s.userCampDAO.GetCurrentSection(req.UserID, req.CampID)
if err != nil {
return &camp.JoinCampResponse{Success: false, Message: fmt.Sprintf("查询当前小节失败: %v", err)}, nil
}
// 如果当前小节ID为空则设置为第一个小节
if currentSectionID == "" {
if err := s.userCampDAO.UpdateCurrentSection(req.UserID, req.CampID, firstSectionID); err != nil {
return &camp.JoinCampResponse{Success: false, Message: fmt.Sprintf("设置当前小节失败: %v", err)}, nil
}
}
}
return &camp.JoinCampResponse{Success: true, Message: "加入成功"}, nil
}
// CheckUserCampStatus 检查用户是否加入了打卡营,并返回打卡营整体状态
func (s *UserCampService) CheckUserCampStatus(userID, campID string) (*camp.CheckUserCampStatusResponse, error) {
if userID == "" || campID == "" {
return &camp.CheckUserCampStatusResponse{
Success: false,
Message: "参数缺失",
IsJoined: false,
CampStatus: camp.CampStatusNotStarted,
}, nil
}
isJoined, joinedAt, currentSectionID, err := s.userCampDAO.CheckUserCampStatus(userID, campID)
if err != nil {
return &camp.CheckUserCampStatusResponse{
Success: false,
Message: fmt.Sprintf("查询失败: %v", err),
IsJoined: false,
CampStatus: camp.CampStatusNotStarted,
}, nil
}
// 格式化时间
formattedJoinedAt := utils.FormatNullTimeToStd(joinedAt)
// 获取当前小节ID
var currentSectionIDStr string
if currentSectionID.Valid {
currentSectionIDStr = currentSectionID.String
}
// 计算打卡营整体状态
campStatus := camp.CampStatusNotStarted
if isJoined {
// 查询总任务数和已完成任务数
totalTasks, completedTasks, err := s.progressDAO.CountTasksAndCompletedByCamp(userID, campID)
if err != nil {
// 查询失败不影响主流程,默认进行中
campStatus = camp.CampStatusInProgress
} else if totalTasks == 0 {
// 没有任务,视为已完成(空营)
campStatus = camp.CampStatusCompleted
} else if completedTasks >= totalTasks {
campStatus = camp.CampStatusCompleted
} else if completedTasks > 0 {
campStatus = camp.CampStatusInProgress
} else {
// 已加入但没有完成任何任务,仍视为进行中(已报名)
campStatus = camp.CampStatusInProgress
}
}
return &camp.CheckUserCampStatusResponse{
Success: true,
Message: "查询成功",
IsJoined: isJoined,
JoinedAt: formattedJoinedAt,
CurrentSectionID: currentSectionIDStr,
CampStatus: campStatus,
}, nil
}
// ListUserCamps 获取用户已加入的打卡营列表
func (s *UserCampService) ListUserCamps(req *camp.ListUserCampsRequest) (*camp.ListUserCampsResponse, error) {
if req.UserID == "" {
return &camp.ListUserCampsResponse{
Success: false,
Message: "用户ID不能为空",
}, nil
}
// 设置默认分页参数
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 10
}
if pageSize > 100 {
pageSize = 100 // 限制最大页面大小
}
joinedCamps, total, err := s.userCampDAO.ListUserJoinedCamps(req.UserID, page, pageSize)
if err != nil {
return &camp.ListUserCampsResponse{
Success: false,
Message: fmt.Sprintf("获取用户打卡营列表失败: %v", err),
}, nil
}
return &camp.ListUserCampsResponse{
Success: true,
Message: "获取成功",
Camps: joinedCamps,
Total: total,
}, nil
}
// ResetCampProgress 清空用户在营内的任务进度(已购访问不变),并写入重置历史、删除该营下客观题答题记录
func (s *UserCampService) ResetCampProgress(req *camp.ResetCampProgressRequest) (*camp.ResetCampProgressResponse, error) {
if req.UserID == "" || req.CampID == "" {
return &camp.ResetCampProgressResponse{Success: false, Message: "参数缺失"}, nil
}
if !req.Confirm {
return &camp.ResetCampProgressResponse{Success: false, Message: "需要确认confirm=true"}, nil
}
// 1. 获取该营下所有任务 ID删除用户在这些任务下的客观题答题记录
var taskIDs []string
if s.taskDAO != nil && s.answerRecordDAO != nil {
tasks, _, err := s.taskDAO.List("", req.CampID, "", camp.TaskTypeUnknown, 1, 10000)
if err == nil {
for _, t := range tasks {
taskIDs = append(taskIDs, t.ID)
}
if len(taskIDs) > 0 {
_, _ = s.answerRecordDAO.DeleteByUserAndTaskIDs(req.UserID, taskIDs)
}
}
}
// 2. 删除进度
cleared, err := s.progressDAO.DeleteByUserAndCamp(req.UserID, req.CampID)
if err != nil {
return &camp.ResetCampProgressResponse{Success: false, Message: fmt.Sprintf("清空进度失败: %v", err)}, nil
}
// 3. 写入重置历史
if s.resetHistoryDAO != nil {
id := snowflake.GetInstance().NextIDString()
note := req.Note
if err := s.resetHistoryDAO.Create(id, req.UserID, req.CampID, int(cleared), note); err != nil {
// 记录失败不阻断重置成功,仅日志或后续可告警
_ = err
}
}
// 4. 将 camp_user_camps.current_section_id 重置为该营第一小节 ID
if s.sectionDAO != nil && s.userCampDAO != nil {
firstSectionID, err := s.sectionDAO.GetFirstSectionID(req.CampID)
if err == nil && firstSectionID != "" {
_ = s.userCampDAO.UpdateCurrentSection(req.UserID, req.CampID, firstSectionID)
}
}
return &camp.ResetCampProgressResponse{Success: true, Message: fmt.Sprintf("重置成功,已清空 %d 条进度记录", cleared)}, nil
}