yxk-20260730
改善提案
普通新增页面不再预生成流水号。提案编号在点击保存时由后端生成,前端仅展示“保存后自动生成”,避免并发重复。
Implementation Changes
新增页面初始化时不再调用年度流水号接口,也不再拼接提案编号。
提案编号输入框继续只读;新增状态显示“保存后自动生成”占位提示。
后端新增专用创建方法,在保存事务内:原子递增年度序列表。
按 提案部门名称-YYYY年-四位流水号 生成编号。
覆盖前端传入的 proposalNo 后保存主表。
新增接口返回 { id, proposalNo };前端附件仍使用返回的 id 关联。
新增年度序列表及历史编号初始化 SQL,保证首次上线从当前最大流水号继续递增。
This commit is contained in:
+5
-4
@@ -105,11 +105,12 @@ public class ImprovementProposalController {
|
||||
ImprovementProposal improvementProposal = new ImprovementProposal();
|
||||
BeanUtils.copyProperties(improvementProposalPage, improvementProposal);
|
||||
improvementProposal.setBpmStatus("1");
|
||||
// 新增申请阶段只创建主表,评分和投票子表由后续流程节点维护。
|
||||
improvementProposalService.saveMain(improvementProposal, null, null);
|
||||
// 附件需要使用主表 ID 关联,不能让前端从成功提示文本中猜测新增记录的 ID。
|
||||
Map<String, String> result = new HashMap<>(1);
|
||||
// 新增申请在保存事务内分配年度流水号,避免前端预取号产生并发重复。
|
||||
improvementProposalService.createNewProposal(improvementProposal);
|
||||
// 附件必须使用已落库的主表 ID 关联,不能使用响应成功文案。
|
||||
Map<String, String> result = new HashMap<>(2);
|
||||
result.put("id", improvementProposal.getId());
|
||||
result.put("proposalNo", improvementProposal.getProposalNo());
|
||||
return Result.OK("添加成功!", result);
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -16,6 +16,13 @@ import java.util.List;
|
||||
public interface ImprovementProposalMapper extends BaseMapper<ImprovementProposal> {
|
||||
|
||||
String selectLatestProposalNoByYear(@Param("year") Integer year);
|
||||
|
||||
/** 原子递增指定年度的提案流水号,首次使用该年度时从 1 开始。 */
|
||||
int incrementProposalYearSequence(@Param("year") Integer year);
|
||||
|
||||
/** 在当前事务内锁定并读取指定年度的最新提案流水号。 */
|
||||
Integer selectProposalYearSequenceForUpdate(@Param("year") Integer year);
|
||||
|
||||
/**
|
||||
* 锁定提案主表,串行化流程发起、编辑和删除操作,避免同一提案生成多个流程实例。
|
||||
*/
|
||||
|
||||
+14
@@ -10,6 +10,20 @@
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- INSERT ... ON DUPLICATE KEY UPDATE 以年度主键为粒度串行递增流水号。 -->
|
||||
<update id="incrementProposalYearSequence">
|
||||
INSERT INTO improvement_proposal_year_sequence (sequence_year, current_sequence)
|
||||
VALUES (#{year}, 1)
|
||||
ON DUPLICATE KEY UPDATE current_sequence = current_sequence + 1
|
||||
</update>
|
||||
|
||||
<select id="selectProposalYearSequenceForUpdate" resultType="java.lang.Integer">
|
||||
SELECT current_sequence
|
||||
FROM improvement_proposal_year_sequence
|
||||
WHERE sequence_year = #{year}
|
||||
FOR UPDATE
|
||||
</select>
|
||||
|
||||
<!-- 角色名单由服务端查询,前端只能从该受控集合中选择。 -->
|
||||
<select id="selectInstituteVoteCandidates" resultType="org.jeecg.modules.improvementproposal.dto.ImprovementInstituteVoteCandidateDTO">
|
||||
SELECT DISTINCT u.id, u.username, u.realname
|
||||
|
||||
+7
@@ -38,6 +38,13 @@ public interface IImprovementProposalService extends IService<ImprovementProposa
|
||||
* @param improvementInstituteVoteList
|
||||
*/
|
||||
public void saveMain(ImprovementProposal improvementProposal,List<ImprovementDeptScore> improvementDeptScoreList,List<ImprovementInstituteVote> improvementInstituteVoteList) ;
|
||||
|
||||
/**
|
||||
* 创建新的改善提案,并在保存事务内生成不可重复的年度提案编号。
|
||||
*
|
||||
* @param improvementProposal 提案主表数据
|
||||
*/
|
||||
void createNewProposal(ImprovementProposal improvementProposal);
|
||||
|
||||
/**
|
||||
* 修改一对多
|
||||
|
||||
+31
@@ -25,6 +25,7 @@ import java.math.RoundingMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
@@ -64,6 +65,7 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
|
||||
private static final String AWARD_SILVER = "1";
|
||||
private static final String AWARD_BRONZE = "2";
|
||||
private static final String AWARD_PARTICIPATION = "3";
|
||||
private static final int PROPOSAL_NO_SEQUENCE_LENGTH = 4;
|
||||
private static final String INSTITUTE_VOTE_RESULT_PASSED = "通过";
|
||||
private static final String INSTITUTE_VOTE_RESULT_FAILED = "不通过";
|
||||
|
||||
@@ -122,6 +124,35 @@ public class ImprovementProposalServiceImpl extends ServiceImpl<ImprovementPropo
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void createNewProposal(ImprovementProposal improvementProposal) {
|
||||
if (oConvertUtils.isEmpty(improvementProposal.getProposalDeptName())) {
|
||||
throw new IllegalStateException("提案部门不能为空,无法生成提案编号");
|
||||
}
|
||||
|
||||
int proposalYear = getProposalYear(improvementProposal.getProposalDate());
|
||||
// 年度控制表通过主键锁串行递增,整个创建事务回滚时流水号也会随之回滚。
|
||||
improvementProposalMapper.incrementProposalYearSequence(proposalYear);
|
||||
Integer sequence = improvementProposalMapper.selectProposalYearSequenceForUpdate(proposalYear);
|
||||
if (sequence == null) {
|
||||
throw new IllegalStateException("生成提案编号失败,请稍后重试");
|
||||
}
|
||||
|
||||
improvementProposal.setProposalNo(formatProposalNo(improvementProposal.getProposalDeptName(), proposalYear, sequence));
|
||||
saveMain(improvementProposal, null, null);
|
||||
}
|
||||
|
||||
private int getProposalYear(Date proposalDate) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(proposalDate == null ? new Date() : proposalDate);
|
||||
return calendar.get(Calendar.YEAR);
|
||||
}
|
||||
|
||||
private String formatProposalNo(String proposalDeptName, int proposalYear, int sequence) {
|
||||
return String.format("%s-%d年-%0" + PROPOSAL_NO_SEQUENCE_LENGTH + "d", proposalDeptName.trim(), proposalYear, sequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveMain(ImprovementProposal improvementProposal, List<ImprovementDeptScore> improvementDeptScoreList,List<ImprovementInstituteVote> improvementInstituteVoteList) {
|
||||
|
||||
Reference in New Issue
Block a user