Add custom request management

This commit is contained in:
王鹏
2026-09-16 16:53:24 +08:00
parent 05e04ce021
commit e7ab83c564
9 changed files with 920 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
package com.ruoyi.app.domain;
import com.ruoyi.common.core.domain.BaseEntity;
/**
* 项目定制需求对象 app_custom_request。
*/
public class AppCustomRequest extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long id;
private String requestNo;
private Long userId;
private String projectName;
private String requirementContent;
private Long techStackId;
/** 保存提交时的技术栈名称,避免后台改名影响历史需求。 */
private String techStackName;
/** 0待审核 1审核中 2已接单 3开发中 4已完成 5审核未通过 */
private Integer status;
private String progressNote;
private Integer pointAmount;
/** 0已扣除 1已退回 */
private Integer pointStatus;
private String rejectReason;
/** 仅用于后台列表展示与查询。 */
private String userName;
private String nickName;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getRequestNo() { return requestNo; }
public void setRequestNo(String requestNo) { this.requestNo = requestNo; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getProjectName() { return projectName; }
public void setProjectName(String projectName) { this.projectName = projectName; }
public String getRequirementContent() { return requirementContent; }
public void setRequirementContent(String requirementContent) { this.requirementContent = requirementContent; }
public Long getTechStackId() { return techStackId; }
public void setTechStackId(Long techStackId) { this.techStackId = techStackId; }
public String getTechStackName() { return techStackName; }
public void setTechStackName(String techStackName) { this.techStackName = techStackName; }
public Integer getStatus() { return status; }
public void setStatus(Integer status) { this.status = status; }
public String getProgressNote() { return progressNote; }
public void setProgressNote(String progressNote) { this.progressNote = progressNote; }
public Integer getPointAmount() { return pointAmount; }
public void setPointAmount(Integer pointAmount) { this.pointAmount = pointAmount; }
public Integer getPointStatus() { return pointStatus; }
public void setPointStatus(Integer pointStatus) { this.pointStatus = pointStatus; }
public String getRejectReason() { return rejectReason; }
public void setRejectReason(String rejectReason) { this.rejectReason = rejectReason; }
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; }
}

View File

@@ -0,0 +1,23 @@
package com.ruoyi.app.mapper;
import java.util.List;
import com.ruoyi.app.domain.AppCustomRequest;
import org.apache.ibatis.annotations.Param;
/**
* 项目定制需求Mapper接口。
*/
public interface AppCustomRequestMapper
{
AppCustomRequest selectAppCustomRequestById(Long id);
AppCustomRequest selectAppCustomRequestByIdForUpdate(Long id);
List<AppCustomRequest> selectAppCustomRequestList(AppCustomRequest request);
List<AppCustomRequest> selectMyCustomRequestList(@Param("userId") Long userId);
int insertAppCustomRequest(AppCustomRequest request);
int updateAppCustomRequest(AppCustomRequest request);
}

View File

@@ -0,0 +1,20 @@
package com.ruoyi.app.service;
import java.util.List;
import com.ruoyi.app.domain.AppCustomRequest;
/**
* 项目定制需求Service接口。
*/
public interface IAppCustomRequestService
{
AppCustomRequest selectAppCustomRequestById(Long id);
List<AppCustomRequest> selectAppCustomRequestList(AppCustomRequest request);
List<AppCustomRequest> selectMyCustomRequestList(Long userId);
AppCustomRequest submitCustomRequest(AppCustomRequest request, Long userId);
int updateCustomRequestStatus(AppCustomRequest request);
}

View File

@@ -0,0 +1,171 @@
package com.ruoyi.app.service.impl;
import java.util.Date;
import java.util.List;
import com.ruoyi.app.domain.AppCustomRequest;
import com.ruoyi.app.domain.AppIntegralRecord;
import com.ruoyi.app.domain.AppTechStack;
import com.ruoyi.app.mapper.AppCustomRequestMapper;
import com.ruoyi.app.mapper.AppIntegralRecordMapper;
import com.ruoyi.app.mapper.AppTechStackMapper;
import com.ruoyi.app.service.IAppCustomRequestService;
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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 项目定制需求Service业务层处理。
*/
@Service
public class AppCustomRequestServiceImpl implements IAppCustomRequestService
{
private static final int SUBMIT_POINTS = 99;
private static final int STATUS_REJECTED = 5;
private static final int POINT_DEDUCTED = 0;
private static final int POINT_REFUNDED = 1;
@Autowired
private AppCustomRequestMapper customRequestMapper;
@Autowired
private AppIntegralRecordMapper integralRecordMapper;
@Autowired
private AppTechStackMapper techStackMapper;
@Autowired
private SysUserMapper sysUserMapper;
@Override
public AppCustomRequest selectAppCustomRequestById(Long id)
{
return customRequestMapper.selectAppCustomRequestById(id);
}
@Override
public List<AppCustomRequest> selectAppCustomRequestList(AppCustomRequest request)
{
return customRequestMapper.selectAppCustomRequestList(request);
}
@Override
public List<AppCustomRequest> selectMyCustomRequestList(Long userId)
{
return customRequestMapper.selectMyCustomRequestList(userId);
}
@Override
@Transactional
public AppCustomRequest submitCustomRequest(AppCustomRequest request, Long userId)
{
String projectName = StringUtils.trim(request.getProjectName());
String requirementContent = StringUtils.trim(request.getRequirementContent());
if (StringUtils.isEmpty(projectName) || projectName.length() < 2 || projectName.length() > 30)
{
throw new ServiceException("项目名称需填写2至30个字");
}
if (StringUtils.isEmpty(requirementContent) || requirementContent.length() < 20 || requirementContent.length() > 500)
{
throw new ServiceException("项目需求需填写20至500个字");
}
if (request.getTechStackId() == null)
{
throw new ServiceException("请选择技术栈");
}
AppTechStack techStack = techStackMapper.selectAppTechStackById(request.getTechStackId());
if (techStack == null || !"0".equals(techStack.getStatus()))
{
throw new ServiceException("所选技术栈已停用,请重新选择");
}
if (sysUserMapper.delIntegralByUserId(SUBMIT_POINTS, userId) != 1)
{
throw new ServiceException("积分不足,提交需求需要99积分");
}
Date now = DateUtils.getNowDate();
request.setRequestNo("DZ" + DateUtils.dateTimeNow() + IdUtils.fastSimpleUUID().substring(0, 6).toUpperCase());
request.setUserId(userId);
request.setProjectName(projectName);
request.setRequirementContent(requirementContent);
request.setTechStackName(techStack.getName());
request.setStatus(0);
request.setProgressNote("需求已提交,等待平台审核");
request.setPointAmount(SUBMIT_POINTS);
request.setPointStatus(POINT_DEDUCTED);
request.setRejectReason(null);
request.setCreateBy(String.valueOf(userId));
request.setCreateTime(now);
request.setUpdateBy(String.valueOf(userId));
request.setUpdateTime(now);
if (customRequestMapper.insertAppCustomRequest(request) != 1)
{
throw new ServiceException("需求提交失败,请重试");
}
insertIntegralRecord(request, 1L, "定制需求提交");
return request;
}
@Override
@Transactional
public int updateCustomRequestStatus(AppCustomRequest update)
{
if (update.getId() == null || update.getStatus() == null || update.getStatus() < 0 || update.getStatus() > 5)
{
throw new ServiceException("需求状态不正确");
}
AppCustomRequest current = customRequestMapper.selectAppCustomRequestByIdForUpdate(update.getId());
if (current == null)
{
throw new ServiceException("定制需求不存在");
}
if (POINT_REFUNDED == current.getPointStatus() && update.getStatus() != STATUS_REJECTED)
{
throw new ServiceException("该需求已退回积分,不能恢复为其他状态");
}
if (update.getStatus() == STATUS_REJECTED && StringUtils.isEmpty(StringUtils.trim(update.getRejectReason())))
{
throw new ServiceException("审核不通过时必须填写原因");
}
current.setStatus(update.getStatus());
current.setProgressNote(StringUtils.trim(update.getProgressNote()));
current.setUpdateBy(update.getUpdateBy());
current.setUpdateTime(DateUtils.getNowDate());
if (update.getStatus() == STATUS_REJECTED)
{
current.setRejectReason(StringUtils.trim(update.getRejectReason()));
if (POINT_DEDUCTED == current.getPointStatus())
{
if (sysUserMapper.addIntegralByUserId(current.getPointAmount(), current.getUserId()) != 1)
{
throw new ServiceException("积分退回失败,请重试");
}
insertIntegralRecord(current, 0L, "定制需求退回");
current.setPointStatus(POINT_REFUNDED);
}
}
else
{
current.setRejectReason("");
}
return customRequestMapper.updateAppCustomRequest(current);
}
private void insertIntegralRecord(AppCustomRequest request, Long isAdd, String source)
{
AppIntegralRecord record = new AppIntegralRecord();
record.setSource(source);
record.setIsAdd(isAdd);
record.setIntegralNumber(request.getPointAmount().longValue());
record.setUserId(request.getUserId());
record.setIntegralTime(new Date());
if (integralRecordMapper.insertAppIntegralRecord(record) != 1)
{
throw new ServiceException("积分流水记录失败,请重试");
}
}
}

View File

@@ -0,0 +1,102 @@
<?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.AppCustomRequestMapper">
<resultMap type="AppCustomRequest" id="AppCustomRequestResult">
<result property="id" column="id" />
<result property="requestNo" column="request_no" />
<result property="userId" column="user_id" />
<result property="projectName" column="project_name" />
<result property="requirementContent" column="requirement_content" />
<result property="techStackId" column="tech_stack_id" />
<result property="techStackName" column="tech_stack_name" />
<result property="status" column="status" />
<result property="progressNote" column="progress_note" />
<result property="pointAmount" column="point_amount" />
<result property="pointStatus" column="point_status" />
<result property="rejectReason" column="reject_reason" />
<result property="userName" column="user_name" />
<result property="nickName" column="nick_name" />
<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="selectAppCustomRequestVo">
select r.id, r.request_no, r.user_id, r.project_name, r.requirement_content,
r.tech_stack_id, r.tech_stack_name,
r.status, r.progress_note, r.point_amount, r.point_status, r.reject_reason,
r.create_by, r.create_time, r.update_by, r.update_time,
u.user_name, u.nick_name
from app_custom_request r
left join sys_user u on u.user_id = r.user_id
</sql>
<select id="selectAppCustomRequestById" parameterType="Long" resultMap="AppCustomRequestResult">
<include refid="selectAppCustomRequestVo" />
where r.id = #{id}
</select>
<select id="selectAppCustomRequestByIdForUpdate" parameterType="Long" resultMap="AppCustomRequestResult">
select id, request_no, user_id, project_name, requirement_content,
tech_stack_id, tech_stack_name, status,
progress_note, point_amount, point_status, reject_reason,
create_by, create_time, update_by, update_time
from app_custom_request
where id = #{id}
for update
</select>
<select id="selectAppCustomRequestList" parameterType="AppCustomRequest" resultMap="AppCustomRequestResult">
<include refid="selectAppCustomRequestVo" />
<where>
<if test="requestNo != null and requestNo != ''">and r.request_no like concat('%', #{requestNo}, '%')</if>
<if test="projectName != null and projectName != ''">and r.project_name like concat('%', #{projectName}, '%')</if>
<if test="techStackName != null and techStackName != ''">and r.tech_stack_name like concat('%', #{techStackName}, '%')</if>
<if test="status != null">and r.status = #{status}</if>
<if test="pointStatus != null">and r.point_status = #{pointStatus}</if>
<if test="userId != null">and r.user_id = #{userId}</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(r.create_time, '%Y%m%d') &gt;= date_format(#{params.beginTime}, '%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''">
and date_format(r.create_time, '%Y%m%d') &lt;= date_format(#{params.endTime}, '%Y%m%d')
</if>
</where>
order by r.update_time desc, r.id desc
</select>
<select id="selectMyCustomRequestList" resultMap="AppCustomRequestResult">
<include refid="selectAppCustomRequestVo" />
where r.user_id = #{userId}
order by r.update_time desc, r.id desc
</select>
<insert id="insertAppCustomRequest" parameterType="AppCustomRequest" useGeneratedKeys="true" keyProperty="id">
insert into app_custom_request
(request_no, user_id, project_name, requirement_content, tech_stack_id, tech_stack_name, status, progress_note,
point_amount, point_status, reject_reason, create_by, create_time, update_by, update_time)
values
(#{requestNo}, #{userId}, #{projectName}, #{requirementContent}, #{techStackId}, #{techStackName}, #{status}, #{progressNote},
#{pointAmount}, #{pointStatus}, #{rejectReason}, #{createBy}, #{createTime}, #{updateBy}, #{updateTime})
</insert>
<update id="updateAppCustomRequest" parameterType="AppCustomRequest">
update app_custom_request
<set>
<if test="status != null">status = #{status},</if>
<if test="progressNote != null">progress_note = #{progressNote},</if>
<if test="pointStatus != null">point_status = #{pointStatus},</if>
<if test="rejectReason != null">reject_reason = #{rejectReason},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
update_time = #{updateTime}
</set>
where id = #{id}
</update>
</mapper>