Add points task submission and review workflow

This commit is contained in:
王鹏
2026-09-16 16:50:05 +08:00
parent 0e24c9e235
commit e31af58d58
24 changed files with 2097 additions and 18 deletions

View File

@@ -54,6 +54,10 @@ public class AppIntegralRecord extends BaseEntity {
private Date integralTime;
private Long fId;
private Long resourceId;
/** 业务类型,用于积分操作幂等。 */
private String bizType;
/** 业务记录主键。 */
private Long bizId;
/**
* 资源标题
@@ -123,6 +127,22 @@ public class AppIntegralRecord extends BaseEntity {
public void setResourceId(Long resourceId) {
this.resourceId = resourceId;
}
public String getBizType() {
return bizType;
}
public void setBizType(String bizType) {
this.bizType = bizType;
}
public Long getBizId() {
return bizId;
}
public void setBizId(Long bizId) {
this.bizId = bizId;
}
public String getResourceTitle() {
return resourceTitle;
@@ -141,6 +161,8 @@ public class AppIntegralRecord extends BaseEntity {
.append("integralNumber", getIntegralNumber())
.append("userId", getUserId())
.append("resourceId", getResourceId())
.append("bizType", getBizType())
.append("bizId", getBizId())
.append("resourceTitle", getResourceTitle())
.append("integralTime", getIntegralTime())
.toString();

View File

@@ -0,0 +1,72 @@
package com.ruoyi.app.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
/** 积分任务。 */
public class AppPointTask extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long id;
private String taskName;
private String taskType;
private String cycleType;
private String coverUrl;
private String description;
private String operationGuide;
private String exampleImages;
private Integer rewardPoints;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
private Integer allowResubmit;
private String status;
private Integer sortOrder;
/** 当前用户在本周期的状态,非数据库字段。 */
private Integer currentStatus;
private Boolean canSubmit;
private Long currentSubmissionId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date nextAvailableAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTaskName() { return taskName; }
public void setTaskName(String taskName) { this.taskName = taskName; }
public String getTaskType() { return taskType; }
public void setTaskType(String taskType) { this.taskType = taskType; }
public String getCycleType() { return cycleType; }
public void setCycleType(String cycleType) { this.cycleType = cycleType; }
public String getCoverUrl() { return coverUrl; }
public void setCoverUrl(String coverUrl) { this.coverUrl = coverUrl; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getOperationGuide() { return operationGuide; }
public void setOperationGuide(String operationGuide) { this.operationGuide = operationGuide; }
public String getExampleImages() { return exampleImages; }
public void setExampleImages(String exampleImages) { this.exampleImages = exampleImages; }
public Integer getRewardPoints() { return rewardPoints; }
public void setRewardPoints(Integer rewardPoints) { this.rewardPoints = rewardPoints; }
public Date getStartTime() { return startTime; }
public void setStartTime(Date startTime) { this.startTime = startTime; }
public Date getEndTime() { return endTime; }
public void setEndTime(Date endTime) { this.endTime = endTime; }
public Integer getAllowResubmit() { return allowResubmit; }
public void setAllowResubmit(Integer allowResubmit) { this.allowResubmit = allowResubmit; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public Integer getSortOrder() { return sortOrder; }
public void setSortOrder(Integer sortOrder) { this.sortOrder = sortOrder; }
public Integer getCurrentStatus() { return currentStatus; }
public void setCurrentStatus(Integer currentStatus) { this.currentStatus = currentStatus; }
public Boolean getCanSubmit() { return canSubmit; }
public void setCanSubmit(Boolean canSubmit) { this.canSubmit = canSubmit; }
public Long getCurrentSubmissionId() { return currentSubmissionId; }
public void setCurrentSubmissionId(Long currentSubmissionId) { this.currentSubmissionId = currentSubmissionId; }
public Date getNextAvailableAt() { return nextAvailableAt; }
public void setNextAvailableAt(Date nextAvailableAt) { this.nextAvailableAt = nextAvailableAt; }
}

View File

@@ -0,0 +1,51 @@
package com.ruoyi.app.domain;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/** 积分任务截图附件。 */
public class AppPointTaskAttachment implements Serializable
{
private static final long serialVersionUID = 1L;
private Long id;
private Long submissionId;
private Long userId;
private String objectKey;
private String originalName;
private Long fileSize;
private String fileType;
private String uploadStatus;
private Integer submitBatch;
private Integer isCurrent;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 七牛临时访问地址,非数据库字段。 */
private String previewUrl;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Long getSubmissionId() { return submissionId; }
public void setSubmissionId(Long submissionId) { this.submissionId = submissionId; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getObjectKey() { return objectKey; }
public void setObjectKey(String objectKey) { this.objectKey = objectKey; }
public String getOriginalName() { return originalName; }
public void setOriginalName(String originalName) { this.originalName = originalName; }
public Long getFileSize() { return fileSize; }
public void setFileSize(Long fileSize) { this.fileSize = fileSize; }
public String getFileType() { return fileType; }
public void setFileType(String fileType) { this.fileType = fileType; }
public String getUploadStatus() { return uploadStatus; }
public void setUploadStatus(String uploadStatus) { this.uploadStatus = uploadStatus; }
public Integer getSubmitBatch() { return submitBatch; }
public void setSubmitBatch(Integer submitBatch) { this.submitBatch = submitBatch; }
public Integer getIsCurrent() { return isCurrent; }
public void setIsCurrent(Integer isCurrent) { this.isCurrent = isCurrent; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
public String getPreviewUrl() { return previewUrl; }
public void setPreviewUrl(String previewUrl) { this.previewUrl = previewUrl; }
}

View File

@@ -0,0 +1,29 @@
package com.ruoyi.app.domain;
import java.io.Serializable;
import java.util.Date;
/** 积分任务审核日志。 */
public class AppPointTaskAuditLog implements Serializable
{
private static final long serialVersionUID = 1L;
private Long submissionId;
private Integer beforeStatus;
private Integer afterStatus;
private String operatorName;
private String remark;
private Date createTime;
public Long getSubmissionId() { return submissionId; }
public void setSubmissionId(Long submissionId) { this.submissionId = submissionId; }
public Integer getBeforeStatus() { return beforeStatus; }
public void setBeforeStatus(Integer beforeStatus) { this.beforeStatus = beforeStatus; }
public Integer getAfterStatus() { return afterStatus; }
public void setAfterStatus(Integer afterStatus) { this.afterStatus = afterStatus; }
public String getOperatorName() { return operatorName; }
public void setOperatorName(String operatorName) { this.operatorName = operatorName; }
public String getRemark() { return remark; }
public void setRemark(String remark) { this.remark = remark; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
}

View File

@@ -0,0 +1,81 @@
package com.ruoyi.app.domain;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
/** 积分任务提交记录。 */
public class AppPointTaskSubmission extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long id;
private String submissionNo;
private Long taskId;
private Long userId;
private String cycleKey;
private Integer rewardPoints;
private Integer status;
private Integer attemptCount;
private String rejectReason;
private String reviewBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reviewTime;
private Long rewardRecordId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date firstSubmitTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastSubmitTime;
private String taskName;
private String taskType;
private String cycleType;
private String userName;
private String nickName;
private List<String> attachmentKeys;
private List<AppPointTaskAttachment> attachments;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getSubmissionNo() { return submissionNo; }
public void setSubmissionNo(String submissionNo) { this.submissionNo = submissionNo; }
public Long getTaskId() { return taskId; }
public void setTaskId(Long taskId) { this.taskId = taskId; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getCycleKey() { return cycleKey; }
public void setCycleKey(String cycleKey) { this.cycleKey = cycleKey; }
public Integer getRewardPoints() { return rewardPoints; }
public void setRewardPoints(Integer rewardPoints) { this.rewardPoints = rewardPoints; }
public Integer getStatus() { return status; }
public void setStatus(Integer status) { this.status = status; }
public Integer getAttemptCount() { return attemptCount; }
public void setAttemptCount(Integer attemptCount) { this.attemptCount = attemptCount; }
public String getRejectReason() { return rejectReason; }
public void setRejectReason(String rejectReason) { this.rejectReason = rejectReason; }
public String getReviewBy() { return reviewBy; }
public void setReviewBy(String reviewBy) { this.reviewBy = reviewBy; }
public Date getReviewTime() { return reviewTime; }
public void setReviewTime(Date reviewTime) { this.reviewTime = reviewTime; }
public Long getRewardRecordId() { return rewardRecordId; }
public void setRewardRecordId(Long rewardRecordId) { this.rewardRecordId = rewardRecordId; }
public Date getFirstSubmitTime() { return firstSubmitTime; }
public void setFirstSubmitTime(Date firstSubmitTime) { this.firstSubmitTime = firstSubmitTime; }
public Date getLastSubmitTime() { return lastSubmitTime; }
public void setLastSubmitTime(Date lastSubmitTime) { this.lastSubmitTime = lastSubmitTime; }
public String getTaskName() { return taskName; }
public void setTaskName(String taskName) { this.taskName = taskName; }
public String getTaskType() { return taskType; }
public void setTaskType(String taskType) { this.taskType = taskType; }
public String getCycleType() { return cycleType; }
public void setCycleType(String cycleType) { this.cycleType = cycleType; }
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
public String getNickName() { return nickName; }
public void setNickName(String nickName) { this.nickName = nickName; }
public List<String> getAttachmentKeys() { return attachmentKeys; }
public void setAttachmentKeys(List<String> attachmentKeys) { this.attachmentKeys = attachmentKeys; }
public List<AppPointTaskAttachment> getAttachments() { return attachments; }
public void setAttachments(List<AppPointTaskAttachment> attachments) { this.attachments = attachments; }
}

View File

@@ -0,0 +1,19 @@
package com.ruoyi.app.domain;
import java.io.Serializable;
/** 七牛任务截图上传凭证请求。 */
public class AppPointTaskUploadRequest implements Serializable
{
private static final long serialVersionUID = 1L;
private String fileName;
private Long fileSize;
private String contentType;
public String getFileName() { return fileName; }
public void setFileName(String fileName) { this.fileName = fileName; }
public Long getFileSize() { return fileSize; }
public void setFileSize(Long fileSize) { this.fileSize = fileSize; }
public String getContentType() { return contentType; }
public void setContentType(String contentType) { this.contentType = contentType; }
}

View File

@@ -0,0 +1,15 @@
package com.ruoyi.app.mapper;
import java.util.List;
import com.ruoyi.app.domain.AppPointTask;
public interface AppPointTaskMapper
{
AppPointTask selectAppPointTaskById(Long id);
List<AppPointTask> selectAppPointTaskList(AppPointTask task);
List<AppPointTask> selectActiveAppPointTaskList();
int insertAppPointTask(AppPointTask task);
int updateAppPointTask(AppPointTask task);
int deleteAppPointTaskByIds(Long[] ids);
int countSubmissionByTaskIds(Long[] ids);
}

View File

@@ -0,0 +1,37 @@
package com.ruoyi.app.mapper;
import java.util.List;
import java.util.Date;
import com.ruoyi.app.domain.AppPointTaskAttachment;
import com.ruoyi.app.domain.AppPointTaskAuditLog;
import com.ruoyi.app.domain.AppPointTaskSubmission;
import org.apache.ibatis.annotations.Param;
public interface AppPointTaskSubmissionMapper
{
AppPointTaskSubmission selectSubmissionById(Long id);
AppPointTaskSubmission selectSubmissionByIdForUpdate(Long id);
AppPointTaskSubmission selectByTaskUserCycle(@Param("taskId") Long taskId,
@Param("userId") Long userId,
@Param("cycleKey") String cycleKey);
List<AppPointTaskSubmission> selectSubmissionList(AppPointTaskSubmission submission);
List<AppPointTaskSubmission> selectMySubmissionList(@Param("userId") Long userId);
int insertSubmission(AppPointTaskSubmission submission);
int updateSubmission(AppPointTaskSubmission submission);
int insertTempAttachment(AppPointTaskAttachment attachment);
Long lockUserForUpload(@Param("userId") Long userId);
int countTempAttachmentsByUser(@Param("userId") Long userId);
int countAttachmentsByUserSince(@Param("userId") Long userId, @Param("since") String since);
List<AppPointTaskAttachment> selectTempAttachments(@Param("userId") Long userId,
@Param("keys") List<String> keys);
int bindAttachments(@Param("userId") Long userId,
@Param("submissionId") Long submissionId,
@Param("submitBatch") Integer submitBatch,
@Param("keys") List<String> keys);
int markAttachmentsNotCurrent(Long submissionId);
List<AppPointTaskAttachment> selectAttachmentsBySubmissionId(Long submissionId);
List<AppPointTaskAttachment> selectExpiredTempAttachments(Date beforeTime);
int deleteTempAttachmentById(Long id);
int insertAuditLog(AppPointTaskAuditLog auditLog);
}

View File

@@ -0,0 +1,28 @@
package com.ruoyi.app.service;
import java.util.List;
import com.ruoyi.app.domain.AppPointTask;
import com.ruoyi.app.domain.AppPointTaskAttachment;
import com.ruoyi.app.domain.AppPointTaskSubmission;
public interface IAppPointTaskService
{
AppPointTask selectAppPointTaskById(Long id);
List<AppPointTask> selectAppPointTaskList(AppPointTask task);
List<AppPointTask> selectActiveTaskList(Long userId);
AppPointTask selectActiveTaskDetail(Long id, Long userId);
int insertAppPointTask(AppPointTask task);
int updateAppPointTask(AppPointTask task);
int deleteAppPointTaskByIds(Long[] ids);
AppPointTaskAttachment createTempAttachment(AppPointTaskAttachment attachment);
AppPointTaskSubmission submitTask(Long taskId, AppPointTaskSubmission request, Long userId);
AppPointTaskSubmission resubmitTask(Long submissionId, AppPointTaskSubmission request, Long userId);
List<AppPointTaskSubmission> selectMySubmissionList(Long userId);
AppPointTaskSubmission selectMySubmissionDetail(Long id, Long userId);
List<AppPointTaskSubmission> selectSubmissionList(AppPointTaskSubmission submission);
AppPointTaskSubmission selectSubmissionDetail(Long id);
int approveSubmission(Long id, String operator);
int rejectSubmission(Long id, String rejectReason, String operator);
int revokeSubmission(Long id, String reason, String operator);
}

View File

@@ -0,0 +1,544 @@
package com.ruoyi.app.service.impl;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import com.ruoyi.app.domain.AppIntegralRecord;
import com.ruoyi.app.domain.AppPointTask;
import com.ruoyi.app.domain.AppPointTaskAttachment;
import com.ruoyi.app.domain.AppPointTaskAuditLog;
import com.ruoyi.app.domain.AppPointTaskSubmission;
import com.ruoyi.app.mapper.AppIntegralRecordMapper;
import com.ruoyi.app.mapper.AppPointTaskMapper;
import com.ruoyi.app.mapper.AppPointTaskSubmissionMapper;
import com.ruoyi.app.service.IAppPointTaskService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AppPointTaskServiceImpl implements IAppPointTaskService
{
private static final String CYCLE_ONCE = "ONCE";
private static final String CYCLE_DAILY = "DAILY";
private static final int STATUS_PENDING = 0;
private static final int STATUS_APPROVED = 1;
private static final int STATUS_REJECTED = 2;
private static final int STATUS_REVOKED = 3;
private static final int MAX_TEMP_ATTACHMENTS_PER_USER = 9;
private static final int MAX_DAILY_ATTACHMENTS_PER_USER = 30;
private static final ZoneId SHANGHAI_ZONE = ZoneId.of("Asia/Shanghai");
@Autowired
private AppPointTaskMapper taskMapper;
@Autowired
private AppPointTaskSubmissionMapper submissionMapper;
@Autowired
private AppIntegralRecordMapper integralRecordMapper;
@Autowired
private SysUserMapper userMapper;
@Override
public AppPointTask selectAppPointTaskById(Long id)
{
return taskMapper.selectAppPointTaskById(id);
}
@Override
public List<AppPointTask> selectAppPointTaskList(AppPointTask task)
{
return taskMapper.selectAppPointTaskList(task);
}
@Override
public List<AppPointTask> selectActiveTaskList(Long userId)
{
List<AppPointTask> tasks = taskMapper.selectActiveAppPointTaskList();
for (AppPointTask task : tasks)
{
populateUserState(task, userId);
}
return tasks;
}
@Override
public AppPointTask selectActiveTaskDetail(Long id, Long userId)
{
AppPointTask task = taskMapper.selectAppPointTaskById(id);
if (task == null)
{
throw new ServiceException("任务不存在");
}
populateUserState(task, userId);
if (!isTaskActive(task, DateUtils.getNowDate()))
{
task.setCanSubmit(false);
}
return task;
}
@Override
public int insertAppPointTask(AppPointTask task)
{
normalizeAndValidateTask(task);
Date now = DateUtils.getNowDate();
task.setCreateTime(now);
task.setUpdateTime(now);
return taskMapper.insertAppPointTask(task);
}
@Override
public int updateAppPointTask(AppPointTask task)
{
if (task.getId() == null)
{
throw new ServiceException("任务不存在");
}
AppPointTask current = taskMapper.selectAppPointTaskById(task.getId());
if (current == null)
{
throw new ServiceException("任务不存在");
}
normalizeAndValidateTask(task);
if (taskMapper.countSubmissionByTaskIds(new Long[] { task.getId() }) > 0
&& (!current.getRewardPoints().equals(task.getRewardPoints())
|| !current.getCycleType().equals(task.getCycleType())))
{
throw new ServiceException("任务已有用户提交,不能修改奖励积分或任务周期");
}
task.setUpdateTime(DateUtils.getNowDate());
return taskMapper.updateAppPointTask(task);
}
@Override
public int deleteAppPointTaskByIds(Long[] ids)
{
if (ids == null || ids.length == 0)
{
return 0;
}
if (taskMapper.countSubmissionByTaskIds(ids) > 0)
{
throw new ServiceException("任务已有用户提交,请停用任务,不要删除");
}
return taskMapper.deleteAppPointTaskByIds(ids);
}
@Override
@Transactional
public AppPointTaskAttachment createTempAttachment(AppPointTaskAttachment attachment)
{
Long userId = attachment.getUserId();
if (userId == null || submissionMapper.lockUserForUpload(userId) == null)
{
throw new ServiceException("用户不存在,请重新登录");
}
if (submissionMapper.countTempAttachmentsByUser(userId) >= MAX_TEMP_ATTACHMENTS_PER_USER)
{
throw new ServiceException("未提交截图已达上限,请先完成任务提交或等待临时截图清理后再试");
}
String todayStart = LocalDate.now(SHANGHAI_ZONE) + " 00:00:00";
if (submissionMapper.countAttachmentsByUserSince(userId, todayStart) >= MAX_DAILY_ATTACHMENTS_PER_USER)
{
throw new ServiceException("今日上传次数已达上限,请明日再试");
}
attachment.setUploadStatus("TEMP");
attachment.setSubmitBatch(0);
attachment.setIsCurrent(0);
attachment.setCreateTime(DateUtils.getNowDate());
if (submissionMapper.insertTempAttachment(attachment) != 1)
{
throw new ServiceException("创建上传记录失败,请重试");
}
return attachment;
}
@Override
@Transactional
public AppPointTaskSubmission submitTask(Long taskId, AppPointTaskSubmission request, Long userId)
{
AppPointTask task = requireActiveTask(taskId);
List<String> keys = normalizeAttachmentKeys(request.getAttachmentKeys());
String cycleKey = cycleKey(task);
if (submissionMapper.selectByTaskUserCycle(taskId, userId, cycleKey) != null)
{
throw new ServiceException(CYCLE_DAILY.equals(task.getCycleType())
? "今日已经提交过该任务" : "该任务已经提交过");
}
assertTempAttachments(userId, keys);
Date now = DateUtils.getNowDate();
AppPointTaskSubmission submission = new AppPointTaskSubmission();
submission.setSubmissionNo("RW" + DateUtils.dateTimeNow() + IdUtils.fastSimpleUUID().substring(0, 6).toUpperCase());
submission.setTaskId(taskId);
submission.setUserId(userId);
submission.setCycleKey(cycleKey);
submission.setRewardPoints(task.getRewardPoints());
submission.setRemark(normalizeRemark(request.getRemark()));
submission.setStatus(STATUS_PENDING);
submission.setAttemptCount(1);
submission.setFirstSubmitTime(now);
submission.setLastSubmitTime(now);
submission.setCreateBy(String.valueOf(userId));
submission.setCreateTime(now);
submission.setUpdateBy(String.valueOf(userId));
submission.setUpdateTime(now);
try
{
if (submissionMapper.insertSubmission(submission) != 1)
{
throw new ServiceException("任务提交失败,请重试");
}
}
catch (DuplicateKeyException ex)
{
throw new ServiceException("该任务本周期已经提交过");
}
bindAttachments(userId, submission.getId(), 1, keys);
insertAudit(submission.getId(), null, STATUS_PENDING, String.valueOf(userId), "用户提交任务");
return selectSubmissionDetail(submission.getId());
}
@Override
@Transactional
public AppPointTaskSubmission resubmitTask(Long submissionId, AppPointTaskSubmission request, Long userId)
{
AppPointTaskSubmission current = submissionMapper.selectSubmissionByIdForUpdate(submissionId);
if (current == null || !userId.equals(current.getUserId()))
{
throw new ServiceException("任务记录不存在");
}
if (current.getStatus() != STATUS_REJECTED)
{
throw new ServiceException("只有审核未通过的任务可以重新提交");
}
AppPointTask task = requireActiveTask(current.getTaskId());
if (CYCLE_DAILY.equals(task.getCycleType()) && !cycleKey(task).equals(current.getCycleKey()))
{
throw new ServiceException("该每日任务周期已结束,请提交今日任务");
}
List<String> keys = normalizeAttachmentKeys(request.getAttachmentKeys());
assertTempAttachments(userId, keys);
int nextBatch = current.getAttemptCount() + 1;
submissionMapper.markAttachmentsNotCurrent(submissionId);
bindAttachments(userId, submissionId, nextBatch, keys);
current.setRemark(normalizeRemark(request.getRemark()));
current.setStatus(STATUS_PENDING);
current.setAttemptCount(nextBatch);
current.setRejectReason(null);
current.setReviewBy(null);
current.setReviewTime(null);
current.setLastSubmitTime(DateUtils.getNowDate());
current.setUpdateBy(String.valueOf(userId));
current.setUpdateTime(DateUtils.getNowDate());
submissionMapper.updateSubmission(current);
insertAudit(submissionId, STATUS_REJECTED, STATUS_PENDING, String.valueOf(userId), "用户重新提交任务");
return selectSubmissionDetail(submissionId);
}
@Override
public List<AppPointTaskSubmission> selectMySubmissionList(Long userId)
{
return submissionMapper.selectMySubmissionList(userId);
}
@Override
public AppPointTaskSubmission selectMySubmissionDetail(Long id, Long userId)
{
AppPointTaskSubmission submission = selectSubmissionDetail(id);
if (submission == null || !userId.equals(submission.getUserId()))
{
throw new ServiceException("任务记录不存在");
}
return submission;
}
@Override
public List<AppPointTaskSubmission> selectSubmissionList(AppPointTaskSubmission submission)
{
return submissionMapper.selectSubmissionList(submission);
}
@Override
public AppPointTaskSubmission selectSubmissionDetail(Long id)
{
AppPointTaskSubmission submission = submissionMapper.selectSubmissionById(id);
if (submission != null)
{
submission.setAttachments(submissionMapper.selectAttachmentsBySubmissionId(id));
}
return submission;
}
@Override
@Transactional
public int approveSubmission(Long id, String operator)
{
AppPointTaskSubmission submission = requirePendingSubmission(id);
AppPointTask task = taskMapper.selectAppPointTaskById(submission.getTaskId());
if (task == null)
{
throw new ServiceException("关联任务不存在");
}
if (userMapper.addIntegralByUserId(submission.getRewardPoints(), submission.getUserId()) != 1)
{
throw new ServiceException("积分发放失败,请重试");
}
AppIntegralRecord record = insertIntegralRecord(submission, 0L, task.getTaskName() + "奖励", "POINT_TASK");
submission.setStatus(STATUS_APPROVED);
submission.setRewardRecordId(record.getId());
submission.setRejectReason(null);
submission.setReviewBy(operator);
submission.setReviewTime(DateUtils.getNowDate());
submission.setUpdateBy(operator);
submission.setUpdateTime(DateUtils.getNowDate());
int rows = submissionMapper.updateSubmission(submission);
insertAudit(id, STATUS_PENDING, STATUS_APPROVED, operator, "审核通过并发放" + submission.getRewardPoints() + "积分");
return rows;
}
@Override
@Transactional
public int rejectSubmission(Long id, String rejectReason, String operator)
{
String reason = StringUtils.trim(rejectReason);
if (StringUtils.isEmpty(reason))
{
throw new ServiceException("审核不通过时必须填写原因");
}
AppPointTaskSubmission submission = requirePendingSubmission(id);
submission.setStatus(STATUS_REJECTED);
submission.setRejectReason(reason);
submission.setReviewBy(operator);
submission.setReviewTime(DateUtils.getNowDate());
submission.setUpdateBy(operator);
submission.setUpdateTime(DateUtils.getNowDate());
int rows = submissionMapper.updateSubmission(submission);
insertAudit(id, STATUS_PENDING, STATUS_REJECTED, operator, reason);
return rows;
}
@Override
@Transactional
public int revokeSubmission(Long id, String reason, String operator)
{
String revokeReason = StringUtils.trim(reason);
if (StringUtils.isEmpty(revokeReason))
{
throw new ServiceException("撤销奖励时必须填写原因");
}
AppPointTaskSubmission submission = submissionMapper.selectSubmissionByIdForUpdate(id);
if (submission == null || submission.getStatus() != STATUS_APPROVED)
{
throw new ServiceException("只有已通过的任务可以撤销奖励");
}
AppPointTask task = taskMapper.selectAppPointTaskById(submission.getTaskId());
if (userMapper.delIntegralByUserId(submission.getRewardPoints(), submission.getUserId()) != 1)
{
throw new ServiceException("用户当前积分不足,无法撤销奖励");
}
insertIntegralRecord(submission, 1L, "撤销:" + (task == null ? "积分任务" : task.getTaskName()), "POINT_TASK_REVOKE");
submission.setStatus(STATUS_REVOKED);
submission.setRejectReason(revokeReason);
submission.setReviewBy(operator);
submission.setReviewTime(DateUtils.getNowDate());
submission.setUpdateBy(operator);
submission.setUpdateTime(DateUtils.getNowDate());
int rows = submissionMapper.updateSubmission(submission);
insertAudit(id, STATUS_APPROVED, STATUS_REVOKED, operator, revokeReason);
return rows;
}
private void populateUserState(AppPointTask task, Long userId)
{
AppPointTaskSubmission submission = submissionMapper.selectByTaskUserCycle(task.getId(), userId, cycleKey(task));
if (submission == null)
{
task.setCanSubmit(true);
return;
}
task.setCurrentStatus(submission.getStatus());
task.setCurrentSubmissionId(submission.getId());
task.setCanSubmit(submission.getStatus() == STATUS_REJECTED);
if (CYCLE_DAILY.equals(task.getCycleType()) && submission.getStatus() == STATUS_APPROVED)
{
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));
calendar.add(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
task.setNextAvailableAt(calendar.getTime());
}
}
private AppPointTask requireActiveTask(Long id)
{
AppPointTask task = taskMapper.selectAppPointTaskById(id);
Date now = DateUtils.getNowDate();
if (task == null || !"0".equals(task.getStatus()))
{
throw new ServiceException("任务不存在或已停用");
}
if (task.getStartTime() != null && task.getStartTime().after(now))
{
throw new ServiceException("任务尚未开始");
}
if (task.getEndTime() != null && task.getEndTime().before(now))
{
throw new ServiceException("任务已经结束");
}
return task;
}
private boolean isTaskActive(AppPointTask task, Date now)
{
return task != null && "0".equals(task.getStatus())
&& (task.getStartTime() == null || !task.getStartTime().after(now))
&& (task.getEndTime() == null || !task.getEndTime().before(now));
}
private AppPointTaskSubmission requirePendingSubmission(Long id)
{
AppPointTaskSubmission submission = submissionMapper.selectSubmissionByIdForUpdate(id);
if (submission == null)
{
throw new ServiceException("任务记录不存在");
}
if (submission.getStatus() != STATUS_PENDING)
{
throw new ServiceException("该任务已经处理,请勿重复操作");
}
return submission;
}
private String cycleKey(AppPointTask task)
{
return CYCLE_DAILY.equals(task.getCycleType()) ? LocalDate.now(SHANGHAI_ZONE).toString() : CYCLE_ONCE;
}
private List<String> normalizeAttachmentKeys(List<String> keys)
{
if (keys == null)
{
throw new ServiceException("请上传任务截图");
}
Set<String> unique = new LinkedHashSet<>();
for (String key : keys)
{
if (StringUtils.isNotEmpty(StringUtils.trim(key)))
{
unique.add(StringUtils.trim(key));
}
}
if (unique.isEmpty() || unique.size() > 3)
{
throw new ServiceException("请上传1至3张任务截图");
}
return new ArrayList<>(unique);
}
private void assertTempAttachments(Long userId, List<String> keys)
{
if (submissionMapper.selectTempAttachments(userId, keys).size() != keys.size())
{
throw new ServiceException("截图无效或已经被其他任务使用,请重新上传");
}
}
private void bindAttachments(Long userId, Long submissionId, Integer batch, List<String> keys)
{
if (submissionMapper.bindAttachments(userId, submissionId, batch, keys) != keys.size())
{
throw new ServiceException("任务截图绑定失败,请重新上传");
}
}
private void insertAudit(Long submissionId, Integer before, Integer after, String operator, String remark)
{
AppPointTaskAuditLog log = new AppPointTaskAuditLog();
log.setSubmissionId(submissionId);
log.setBeforeStatus(before);
log.setAfterStatus(after);
log.setOperatorName(operator);
log.setRemark(remark);
log.setCreateTime(DateUtils.getNowDate());
submissionMapper.insertAuditLog(log);
}
private AppIntegralRecord insertIntegralRecord(AppPointTaskSubmission submission, Long isAdd, String source, String bizType)
{
AppIntegralRecord record = new AppIntegralRecord();
record.setSource(source);
record.setIsAdd(isAdd);
record.setIntegralNumber(submission.getRewardPoints().longValue());
record.setUserId(submission.getUserId());
record.setIntegralTime(DateUtils.getNowDate());
record.setBizType(bizType);
record.setBizId(submission.getId());
if (integralRecordMapper.insertAppIntegralRecord(record) != 1)
{
throw new ServiceException("积分流水记录失败,请重试");
}
return record;
}
private void normalizeAndValidateTask(AppPointTask task)
{
task.setTaskName(StringUtils.trim(task.getTaskName()));
task.setDescription(StringUtils.trim(task.getDescription()));
task.setOperationGuide(StringUtils.trim(task.getOperationGuide()));
if (StringUtils.isEmpty(task.getTaskName()) || task.getTaskName().length() > 80)
{
throw new ServiceException("任务名称需填写1至80个字");
}
if (!CYCLE_ONCE.equals(task.getCycleType()) && !CYCLE_DAILY.equals(task.getCycleType()))
{
throw new ServiceException("任务周期不正确");
}
if (task.getRewardPoints() == null || task.getRewardPoints() <= 0 || task.getRewardPoints() > 100000)
{
throw new ServiceException("奖励积分必须在1至100000之间");
}
if (task.getStartTime() != null && task.getEndTime() != null && !task.getStartTime().before(task.getEndTime()))
{
throw new ServiceException("结束时间必须晚于开始时间");
}
task.setAllowResubmit(1);
if (task.getSortOrder() == null || task.getSortOrder() < 0)
{
task.setSortOrder(0);
}
if (!"1".equals(task.getStatus()))
{
task.setStatus("0");
}
if (StringUtils.isEmpty(task.getTaskType()))
{
task.setTaskType("OTHER");
}
}
private String normalizeRemark(String remark)
{
String value = StringUtils.trim(remark);
if (value != null && value.length() > 300)
{
throw new ServiceException("补充说明不能超过300个字");
}
return value;
}
}

View File

@@ -11,12 +11,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="integralNumber" column="integral_number" />
<result property="userId" column="user_id" />
<result property="resourceId" column="resource_id" />
<result property="bizType" column="biz_type" />
<result property="bizId" column="biz_id" />
<result property="resourceTitle" column="resource_title" />
<result property="integralTime" column="integral_time" />
</resultMap>
<sql id="selectAppIntegralRecordVo">
select a.id, a.source, a.is_add, a.integral_number, a.user_id, a.resource_id, a.integral_time, r.resource_title
select a.id, a.source, a.is_add, a.integral_number, a.user_id, a.resource_id,
a.biz_type, a.biz_id, a.integral_time, r.resource_title
from app_integral_record a
left join app_resource r on a.resource_id = r.id
</sql>
@@ -68,6 +71,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="integralNumber != null">integral_number,</if>
<if test="userId != null">user_id,</if>
<if test="resourceId != null">resource_id,</if>
<if test="bizType != null">biz_type,</if>
<if test="bizId != null">biz_id,</if>
<if test="integralTime != null">integral_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
@@ -76,6 +81,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="integralNumber != null">#{integralNumber},</if>
<if test="userId != null">#{userId},</if>
<if test="resourceId != null">#{resourceId},</if>
<if test="bizType != null">#{bizType},</if>
<if test="bizId != null">#{bizId},</if>
<if test="integralTime != null">#{integralTime},</if>
</trim>
</insert>
@@ -88,6 +95,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="integralNumber != null">integral_number = #{integralNumber},</if>
<if test="userId != null">user_id = #{userId},</if>
<if test="resourceId != null">resource_id = #{resourceId},</if>
<if test="bizType != null">biz_type = #{bizType},</if>
<if test="bizId != null">biz_id = #{bizId},</if>
<if test="integralTime != null">integral_time = #{integralTime},</if>
</trim>
where id = #{id}

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.app.mapper.AppPointTaskMapper">
<resultMap type="AppPointTask" id="AppPointTaskResult">
<result property="id" column="id" />
<result property="taskName" column="task_name" />
<result property="taskType" column="task_type" />
<result property="cycleType" column="cycle_type" />
<result property="coverUrl" column="cover_url" />
<result property="description" column="description" />
<result property="operationGuide" column="operation_guide" />
<result property="exampleImages" column="example_images" />
<result property="rewardPoints" column="reward_points" />
<result property="startTime" column="start_time" />
<result property="endTime" column="end_time" />
<result property="allowResubmit" column="allow_resubmit" />
<result property="status" column="status" />
<result property="sortOrder" column="sort_order" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="selectTaskVo">
select id, task_name, task_type, cycle_type, cover_url, description, operation_guide,
example_images, reward_points, start_time, end_time, allow_resubmit, status,
sort_order, create_by, create_time, update_by, update_time
from app_point_task
</sql>
<select id="selectAppPointTaskById" resultMap="AppPointTaskResult">
<include refid="selectTaskVo" /> where id = #{id}
</select>
<select id="selectAppPointTaskList" parameterType="AppPointTask" resultMap="AppPointTaskResult">
<include refid="selectTaskVo" />
<where>
<if test="taskName != null and taskName != ''">and task_name like concat('%', #{taskName}, '%')</if>
<if test="taskType != null and taskType != ''">and task_type = #{taskType}</if>
<if test="cycleType != null and cycleType != ''">and cycle_type = #{cycleType}</if>
<if test="status != null and status != ''">and status = #{status}</if>
</where>
order by sort_order asc, id desc
</select>
<select id="selectActiveAppPointTaskList" resultMap="AppPointTaskResult">
<include refid="selectTaskVo" />
where status = '0'
and (start_time is null or start_time &lt;= now())
and (end_time is null or end_time &gt;= now())
order by sort_order asc, id desc
</select>
<insert id="insertAppPointTask" parameterType="AppPointTask" useGeneratedKeys="true" keyProperty="id">
insert into app_point_task
(task_name, task_type, cycle_type, cover_url, description, operation_guide, example_images,
reward_points, start_time, end_time, allow_resubmit, status, sort_order,
create_by, create_time, update_by, update_time)
values
(#{taskName}, #{taskType}, #{cycleType}, #{coverUrl}, #{description}, #{operationGuide}, #{exampleImages},
#{rewardPoints}, #{startTime}, #{endTime}, #{allowResubmit}, #{status}, #{sortOrder},
#{createBy}, #{createTime}, #{updateBy}, #{updateTime})
</insert>
<update id="updateAppPointTask" parameterType="AppPointTask">
update app_point_task
<set>
<if test="taskName != null">task_name = #{taskName},</if>
<if test="taskType != null">task_type = #{taskType},</if>
<if test="cycleType != null">cycle_type = #{cycleType},</if>
<if test="coverUrl != null">cover_url = #{coverUrl},</if>
<if test="description != null">description = #{description},</if>
<if test="operationGuide != null">operation_guide = #{operationGuide},</if>
<if test="exampleImages != null">example_images = #{exampleImages},</if>
<if test="rewardPoints != null">reward_points = #{rewardPoints},</if>
start_time = #{startTime},
end_time = #{endTime},
<if test="allowResubmit != null">allow_resubmit = #{allowResubmit},</if>
<if test="status != null">status = #{status},</if>
<if test="sortOrder != null">sort_order = #{sortOrder},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
update_time = #{updateTime}
</set>
where id = #{id}
</update>
<delete id="deleteAppPointTaskByIds">
delete from app_point_task where id in
<foreach item="id" collection="array" open="(" separator="," close=")">#{id}</foreach>
</delete>
<select id="countSubmissionByTaskIds" resultType="int">
select count(1) from app_point_task_submission where task_id in
<foreach item="id" collection="array" open="(" separator="," close=")">#{id}</foreach>
</select>
</mapper>

View File

@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.app.mapper.AppPointTaskSubmissionMapper">
<resultMap type="AppPointTaskSubmission" id="SubmissionResult">
<result property="id" column="id" />
<result property="submissionNo" column="submission_no" />
<result property="taskId" column="task_id" />
<result property="userId" column="user_id" />
<result property="cycleKey" column="cycle_key" />
<result property="rewardPoints" column="reward_points" />
<result property="remark" column="remark" />
<result property="status" column="status" />
<result property="attemptCount" column="attempt_count" />
<result property="rejectReason" column="reject_reason" />
<result property="reviewBy" column="review_by" />
<result property="reviewTime" column="review_time" />
<result property="rewardRecordId" column="reward_record_id" />
<result property="firstSubmitTime" column="first_submit_time" />
<result property="lastSubmitTime" column="last_submit_time" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="taskName" column="task_name" />
<result property="taskType" column="task_type" />
<result property="cycleType" column="cycle_type" />
<result property="userName" column="user_name" />
<result property="nickName" column="nick_name" />
</resultMap>
<resultMap type="AppPointTaskAttachment" id="AttachmentResult">
<result property="id" column="id" />
<result property="submissionId" column="submission_id" />
<result property="userId" column="user_id" />
<result property="objectKey" column="object_key" />
<result property="originalName" column="original_name" />
<result property="fileSize" column="file_size" />
<result property="fileType" column="file_type" />
<result property="uploadStatus" column="upload_status" />
<result property="submitBatch" column="submit_batch" />
<result property="isCurrent" column="is_current" />
<result property="createTime" column="create_time" />
</resultMap>
<sql id="selectSubmissionVo">
select s.id, s.submission_no, s.task_id, s.user_id, s.cycle_key, s.reward_points,
s.remark, s.status, s.attempt_count, s.reject_reason, s.review_by, s.review_time,
s.reward_record_id, s.first_submit_time, s.last_submit_time,
s.create_by, s.create_time, s.update_by, s.update_time,
t.task_name, t.task_type, t.cycle_type, u.user_name, u.nick_name
from app_point_task_submission s
left join app_point_task t on t.id = s.task_id
left join sys_user u on u.user_id = s.user_id
</sql>
<select id="selectSubmissionById" resultMap="SubmissionResult">
<include refid="selectSubmissionVo" /> where s.id = #{id}
</select>
<select id="selectSubmissionByIdForUpdate" resultMap="SubmissionResult">
select id, submission_no, task_id, user_id, cycle_key, reward_points, remark, status,
attempt_count, reject_reason, review_by, review_time, reward_record_id,
first_submit_time, last_submit_time, create_by, create_time, update_by, update_time
from app_point_task_submission where id = #{id} for update
</select>
<select id="selectByTaskUserCycle" resultMap="SubmissionResult">
select id, submission_no, task_id, user_id, cycle_key, reward_points, remark, status,
attempt_count, reject_reason, review_by, review_time, reward_record_id,
first_submit_time, last_submit_time, create_by, create_time, update_by, update_time
from app_point_task_submission
where task_id = #{taskId} and user_id = #{userId} and cycle_key = #{cycleKey}
</select>
<select id="selectSubmissionList" parameterType="AppPointTaskSubmission" resultMap="SubmissionResult">
<include refid="selectSubmissionVo" />
<where>
<if test="submissionNo != null and submissionNo != ''">and s.submission_no like concat('%', #{submissionNo}, '%')</if>
<if test="taskId != null">and s.task_id = #{taskId}</if>
<if test="taskName != null and taskName != ''">and t.task_name like concat('%', #{taskName}, '%')</if>
<if test="status != null">and s.status = #{status}</if>
<if test="cycleKey != null and cycleKey != ''">and s.cycle_key = #{cycleKey}</if>
<if test="userName != null and userName != ''">
and (u.user_name like concat('%', #{userName}, '%') or u.nick_name like concat('%', #{userName}, '%'))
</if>
<if test="params.beginTime != null and params.beginTime != ''">
and date_format(s.last_submit_time, '%Y%m%d') &gt;= date_format(#{params.beginTime}, '%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''">
and date_format(s.last_submit_time, '%Y%m%d') &lt;= date_format(#{params.endTime}, '%Y%m%d')
</if>
</where>
order by s.last_submit_time desc, s.id desc
</select>
<select id="selectMySubmissionList" resultMap="SubmissionResult">
<include refid="selectSubmissionVo" />
where s.user_id = #{userId}
order by s.last_submit_time desc, s.id desc
</select>
<insert id="insertSubmission" parameterType="AppPointTaskSubmission" useGeneratedKeys="true" keyProperty="id">
insert into app_point_task_submission
(submission_no, task_id, user_id, cycle_key, reward_points, remark, status, attempt_count,
reject_reason, review_by, review_time, reward_record_id, first_submit_time, last_submit_time,
create_by, create_time, update_by, update_time)
values
(#{submissionNo}, #{taskId}, #{userId}, #{cycleKey}, #{rewardPoints}, #{remark}, #{status}, #{attemptCount},
#{rejectReason}, #{reviewBy}, #{reviewTime}, #{rewardRecordId}, #{firstSubmitTime}, #{lastSubmitTime},
#{createBy}, #{createTime}, #{updateBy}, #{updateTime})
</insert>
<update id="updateSubmission" parameterType="AppPointTaskSubmission">
update app_point_task_submission
<set>
<if test="remark != null">remark = #{remark},</if>
<if test="status != null">status = #{status},</if>
<if test="attemptCount != null">attempt_count = #{attemptCount},</if>
reject_reason = #{rejectReason},
review_by = #{reviewBy},
review_time = #{reviewTime},
<if test="rewardRecordId != null">reward_record_id = #{rewardRecordId},</if>
<if test="lastSubmitTime != null">last_submit_time = #{lastSubmitTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
update_time = #{updateTime}
</set>
where id = #{id}
</update>
<insert id="insertTempAttachment" parameterType="AppPointTaskAttachment" useGeneratedKeys="true" keyProperty="id">
insert into app_point_task_attachment
(submission_id, user_id, object_key, original_name, file_size, file_type,
upload_status, submit_batch, is_current, create_time)
values
(null, #{userId}, #{objectKey}, #{originalName}, #{fileSize}, #{fileType},
'TEMP', 0, 0, #{createTime})
</insert>
<select id="lockUserForUpload" resultType="long">
select user_id from sys_user where user_id = #{userId} for update
</select>
<select id="countTempAttachmentsByUser" resultType="int">
select count(1)
from app_point_task_attachment
where user_id = #{userId} and upload_status = 'TEMP'
</select>
<select id="countAttachmentsByUserSince" resultType="int">
select count(1)
from app_point_task_attachment
where user_id = #{userId} and create_time &gt;= #{since}
</select>
<select id="selectTempAttachments" resultMap="AttachmentResult">
select id, submission_id, user_id, object_key, original_name, file_size, file_type,
upload_status, submit_batch, is_current, create_time
from app_point_task_attachment
where user_id = #{userId} and upload_status = 'TEMP' and object_key in
<foreach item="key" collection="keys" open="(" separator="," close=")">#{key}</foreach>
</select>
<update id="bindAttachments">
update app_point_task_attachment
set submission_id = #{submissionId}, upload_status = 'BOUND', submit_batch = #{submitBatch}, is_current = 1
where user_id = #{userId} and upload_status = 'TEMP' and object_key in
<foreach item="key" collection="keys" open="(" separator="," close=")">#{key}</foreach>
</update>
<update id="markAttachmentsNotCurrent">
update app_point_task_attachment set is_current = 0 where submission_id = #{submissionId}
</update>
<select id="selectAttachmentsBySubmissionId" resultMap="AttachmentResult">
select id, submission_id, user_id, object_key, original_name, file_size, file_type,
upload_status, submit_batch, is_current, create_time
from app_point_task_attachment
where submission_id = #{submissionId}
order by submit_batch desc, id asc
</select>
<select id="selectExpiredTempAttachments" resultMap="AttachmentResult">
select id, submission_id, user_id, object_key, original_name, file_size, file_type,
upload_status, submit_batch, is_current, create_time
from app_point_task_attachment
where upload_status = 'TEMP' and create_time &lt; #{beforeTime}
order by id asc
limit 500
</select>
<delete id="deleteTempAttachmentById">
delete from app_point_task_attachment where id = #{id} and upload_status = 'TEMP'
</delete>
<insert id="insertAuditLog" parameterType="AppPointTaskAuditLog">
insert into app_point_task_audit_log
(submission_id, before_status, after_status, operator_name, remark, create_time)
values
(#{submissionId}, #{beforeStatus}, #{afterStatus}, #{operatorName}, #{remark}, #{createTime})
</insert>
</mapper>