package daytask import ( "app/commons/model/entity" "app/commons/services" "encoding/json" "time" "github.com/gin-gonic/gin" ) // TaskApply 领取任务 func (s *Server) TaskApply(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() userId := ctx.UserId() type ApplyRequest struct { TaskId int64 `json:"taskId" binding:"required"` } var req ApplyRequest if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 获取任务 task := &entity.DtTask{} if err := db.Where("id = ? AND status = ?", req.TaskId, 1).First(task).Error; err != nil { ctx.Fail("task_not_found") return } // 检查任务剩余数量 if task.RemainCount <= 0 { ctx.Fail("task_sold_out") return } // 获取用户 user := &entity.DtUser{} if err := db.Where("id = ?", userId).First(user).Error; err != nil { ctx.Fail("user_not_found") return } // 检查用户等级 if task.MinLevel > 0 { level := &entity.DtUserLevel{} db.Where("id = ?", user.LevelId).First(level) if level.Level < task.MinLevel { ctx.Fail("level_not_enough") return } } // 检查用户今日领取数量(0表示不限制) if task.DailyLimit > 0 { today := time.Now().Format("2006-01-02") var todayCount int64 db.Model(&entity.DtUserTask{}). Where("user_id = ? AND task_id = ? AND DATE(FROM_UNIXTIME(created_at)) = ?", userId, req.TaskId, today). Count(&todayCount) if int(todayCount) >= task.DailyLimit { ctx.Fail("daily_limit_reached") return } } // 检查用户总领取数量(0表示不限制) if task.TotalLimit > 0 { var totalCount int64 db.Model(&entity.DtUserTask{}). Where("user_id = ? AND task_id = ? AND status != ?", userId, req.TaskId, entity.UserTaskStatusAbandoned). Count(&totalCount) if int(totalCount) >= task.TotalLimit { ctx.Fail("total_limit_reached") return } } // 检查是否有进行中或待审核的任务 var pendingCount int64 db.Model(&entity.DtUserTask{}). Where("user_id = ? AND task_id = ? AND status IN (?, ?)", userId, req.TaskId, entity.UserTaskStatusPending, entity.UserTaskStatusSubmitted). Count(&pendingCount) if pendingCount > 0 { ctx.Fail("task_in_progress") return } // 事务:创建任务记录 + 扣减剩余数量 tx := db.Begin() // 先原子扣减剩余数量,防止超领 result := tx.Model(&entity.DtTask{}). Where("id = ? AND remain_count > 0", req.TaskId). UpdateColumn("remain_count", tx.Raw("remain_count - 1")) if result.Error != nil || result.RowsAffected == 0 { tx.Rollback() ctx.Fail("task_sold_out") return } // 创建任务记录 userTask := &entity.DtUserTask{ UserId: userId, TaskId: req.TaskId, TaskNo: task.TaskNo, TaskTitle: task.Title, RewardAmount: task.RewardAmount, Status: entity.UserTaskStatusPending, } if err := tx.Create(userTask).Error; err != nil { tx.Rollback() ctx.Fail("claim_failed") return } tx.Commit() ctx.OK(userTask) } // TaskSubmit 提交任务 func (s *Server) TaskSubmit(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() userId := ctx.UserId() type SubmitRequest struct { UserTaskId int64 `json:"userTaskId" binding:"required"` Screenshots []string `json:"screenshots" binding:"required"` Remark string `json:"remark"` } var req SubmitRequest if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 获取用户任务 userTask := &entity.DtUserTask{} if err := db.Where("id = ? AND user_id = ?", req.UserTaskId, userId).First(userTask).Error; err != nil { ctx.Fail("user_task_not_found") return } // 检查状态:进行中(0)、审核失败(-1)、已拒绝(3)或已打回(5)的任务可以提交 if userTask.Status != entity.UserTaskStatusPending && userTask.Status != entity.UserTaskStatusFailed && userTask.Status != entity.UserTaskStatusRejected && userTask.Status != entity.UserTaskStatusReturnBack { ctx.Fail("task_cannot_submit") return } // 获取任务检查是否需要截图 task := &entity.DtTask{} db.Where("id = ?", userTask.TaskId).First(task) if task.RequireScreenshot == 1 && len(req.Screenshots) == 0 { ctx.Fail("screenshot_required") return } // 更新任务 screenshotsJson, _ := json.Marshal(req.Screenshots) db.Model(&entity.DtUserTask{}). Where("id = ?", req.UserTaskId). Updates(map[string]interface{}{ "screenshots": string(screenshotsJson), "remark": req.Remark, "submit_time": time.Now().Unix(), "status": entity.UserTaskStatusSubmitted, "reject_reason": "", // 重新提交时清空打回原因 }) ctx.OK(nil) } // TaskSupplementScreenshots 补充截图(待审核状态可以补充) func (s *Server) TaskSupplementScreenshots(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() userId := ctx.UserId() type SupplementRequest struct { UserTaskId int64 `json:"userTaskId" binding:"required"` Screenshots []string `json:"screenshots" binding:"required"` // 新增的截图 } var req SupplementRequest if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 获取用户任务 userTask := &entity.DtUserTask{} if err := db.Where("id = ? AND user_id = ?", req.UserTaskId, userId).First(userTask).Error; err != nil { ctx.Fail("user_task_not_found") return } // 检查状态:只有待审核(1)的任务可以补充截图 if userTask.Status != entity.UserTaskStatusSubmitted { ctx.Fail("task_cannot_supplement") return } // 解析现有截图 existingScreenshots := make([]string, 0) if userTask.Screenshots != "" { json.Unmarshal([]byte(userTask.Screenshots), &existingScreenshots) } // 合并截图 allScreenshots := append(existingScreenshots, req.Screenshots...) screenshotsJson, _ := json.Marshal(allScreenshots) // 更新任务 db.Model(&entity.DtUserTask{}). Where("id = ?", req.UserTaskId). Update("screenshots", string(screenshotsJson)) ctx.OK(gin.H{ "screenshots": allScreenshots, }) } // TaskAbandon 放弃任务 func (s *Server) TaskAbandon(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() userId := ctx.UserId() type AbandonRequest struct { UserTaskId int64 `json:"userTaskId" binding:"required"` } var req AbandonRequest if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 获取用户任务 userTask := &entity.DtUserTask{} if err := db.Where("id = ? AND user_id = ?", req.UserTaskId, userId).First(userTask).Error; err != nil { ctx.Fail("user_task_not_found") return } // 只有进行中的任务可以放弃 if userTask.Status != entity.UserTaskStatusPending { ctx.Fail("task_cannot_abandon") return } // 事务:更新状态 + 恢复数量 tx := db.Begin() tx.Model(&entity.DtUserTask{}). Where("id = ?", req.UserTaskId). Update("status", entity.UserTaskStatusAbandoned) tx.Model(&entity.DtTask{}). Where("id = ?", userTask.TaskId). UpdateColumn("remain_count", tx.Raw("remain_count + 1")) tx.Commit() ctx.OK(nil) } // TaskMy 我的任务列表 func (s *Server) TaskMy(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() userId := ctx.UserId() status := ctx.QueryInt64("status", -99) // -99表示全部 paging := &services.Pagination{ Current: ctx.QueryInt64("current", 1), Size: ctx.QueryInt64("size", 20), } query := db.Model(&entity.DtUserTask{}).Where("user_id = ?", userId) if status != -99 { query = query.Where("status = ?", status) } query.Count(&paging.Total) paging.Computer() userTasks := make([]*entity.DtUserTask, 0) query.Order("created_at DESC"). Offset(int(paging.Start)). Limit(int(paging.Size)). Find(&userTasks) // 批量获取关联的任务信息(避免N+1查询) type TaskWithInfo struct { *entity.DtUserTask Task *entity.DtTask `json:"task"` } // 收集所有 taskId taskIds := make([]int64, 0, len(userTasks)) for _, ut := range userTasks { taskIds = append(taskIds, ut.TaskId) } // 一次性查询所有任务 taskMap := make(map[int64]*entity.DtTask) if len(taskIds) > 0 { tasks := make([]*entity.DtTask, 0) db.Where("id IN ?", taskIds).Find(&tasks) for _, t := range tasks { taskMap[t.Id] = t } } result := make([]*TaskWithInfo, 0) for _, ut := range userTasks { task := taskMap[ut.TaskId] if task == nil { task = &entity.DtTask{} } result = append(result, &TaskWithInfo{ DtUserTask: ut, Task: task, }) } ctx.OK(gin.H{ "list": result, "paging": paging, }) } // TaskMyDetail 我的任务详情 func (s *Server) TaskMyDetail(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() userId := ctx.UserId() userTaskId := ctx.QueryInt64("id", 0) if userTaskId == 0 { ctx.Fail("id_required") return } // 获取用户任务 userTask := &entity.DtUserTask{} if err := db.Where("id = ? AND user_id = ?", userTaskId, userId).First(userTask).Error; err != nil { ctx.Fail("user_task_not_found") return } // 获取任务 task := &entity.DtTask{} db.Where("id = ?", userTask.TaskId).First(task) // 获取任务分类 category := &entity.DtTaskCategory{} db.Where("id = ?", task.CategoryId).First(category) // 获取任务步骤 steps := make([]*entity.DtTaskStep, 0) db.Model(&entity.DtTaskStep{}). Where("task_id = ?", userTask.TaskId). Order("step_no ASC"). Find(&steps) ctx.OK(gin.H{ "userTask": userTask, "task": task, "category": category, "steps": steps, }) }