224 lines
7.2 KiB
Go
224 lines
7.2 KiB
Go
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
|
||
}
|
||
|