552 lines
22 KiB
Java
552 lines
22 KiB
Java
package com.gxwebsoft.approval.service;
|
||
|
||
import cn.hutool.core.util.StrUtil;
|
||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||
import com.gxwebsoft.approval.constant.ApprovalConstants;
|
||
import com.gxwebsoft.approval.entity.ApprovalInstance;
|
||
import com.gxwebsoft.approval.entity.ApprovalTask;
|
||
import com.gxwebsoft.approval.mapper.ApprovalInstanceMapper;
|
||
import com.gxwebsoft.approval.mapper.ApprovalTaskMapper;
|
||
import com.gxwebsoft.approval.param.ApprovalTaskAssignParam;
|
||
import com.gxwebsoft.approval.support.ApprovalApprover;
|
||
import com.gxwebsoft.approval.support.ApprovalNode;
|
||
import com.gxwebsoft.approval.vo.ApprovalAssignVO;
|
||
import com.gxwebsoft.approval.vo.ApprovalUserVO;
|
||
import com.gxwebsoft.common.core.exception.BusinessException;
|
||
import com.gxwebsoft.common.system.entity.User;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.transaction.PlatformTransactionManager;
|
||
import org.springframework.transaction.support.TransactionTemplate;
|
||
|
||
import javax.annotation.Resource;
|
||
import java.time.LocalDateTime;
|
||
import java.util.ArrayList;
|
||
import java.util.Collections;
|
||
import java.util.List;
|
||
import java.util.function.Supplier;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* 审批任务动作:通过、驳回、撤回与节点推进。
|
||
*
|
||
* <p>跨库约束:审批人解析必须走 {@link ApprovalDirectoryService}(core 专用连接),
|
||
* 在事务外完成;写 master 的部分放进 TransactionTemplate。任务处理使用
|
||
* WHERE status='PENDING' 的条件更新,抢先后到者收到「任务已处理」。</p>
|
||
*/
|
||
@Service
|
||
public class ApprovalTaskService {
|
||
|
||
private static final String RESOLVER_REPORTER_ORG_LEADER = "reporterOrgLeader";
|
||
private static final String RESOLVER_ACCEPT_ORG_LEADER = "acceptOrgLeader";
|
||
private static final String RESOLVER_ARCHIVIST = "archivist";
|
||
private static final String RESOLVER_REPORTER = "reporter";
|
||
private static final String RESOLVER_REPORTER_AND_DISPATCH_TARGET = "reporterAndDispatchTarget";
|
||
private static final String RESOLVER_ACCEPT_ORG_MEMBERS = "acceptOrgMembers";
|
||
private static final int MAX_OPINION_LENGTH = 500;
|
||
|
||
@Resource
|
||
private ApprovalInstanceMapper instanceMapper;
|
||
@Resource
|
||
private ApprovalTaskMapper taskMapper;
|
||
@Resource
|
||
private ApprovalEngineService engineService;
|
||
@Resource
|
||
private ApprovalDirectoryService directoryService;
|
||
@Resource
|
||
private PlatformTransactionManager transactionManager;
|
||
|
||
/**
|
||
* 通过:APPROVE 节点或签,处理后取消同节点同轮其余待办并推进下一节点。
|
||
*/
|
||
public Result approve(Long taskId, String opinion, User user) {
|
||
ApprovalTask task = requireTask(taskId, user);
|
||
if (!ApprovalConstants.NODE_APPROVE.equals(task.getNodeType())) {
|
||
throw new BusinessException("当前任务不支持通过");
|
||
}
|
||
String normalized = normalizeOpinion(opinion);
|
||
ApprovalInstance instance = instanceMapper.selectById(task.getInstanceId());
|
||
if (instance == null) {
|
||
throw new BusinessException("单据不存在");
|
||
}
|
||
ApprovalNode next = engineService.nextNode(instance, task.getNodeKey());
|
||
List<ApprovalApprover> approvers =
|
||
next == null ? Collections.emptyList() : resolveApprovers(instance, next);
|
||
|
||
String warning = inTransaction(() -> doApprove(taskId, task, normalized, user, next, approvers));
|
||
return new Result(instance.getId(), warning);
|
||
}
|
||
|
||
/**
|
||
* 存档确认:CONFIRM 任务只能确认(无意见),同节点同轮全部完成后实例进入审批完成,
|
||
* 并激活归档节点;归档节点缺配时实例停在审批中并提示管理员补配置。
|
||
*/
|
||
public Result confirm(Long taskId, User user) {
|
||
ApprovalTask task = requireTask(taskId, user);
|
||
if (!ApprovalConstants.NODE_CONFIRM.equals(task.getNodeType())) {
|
||
throw new BusinessException("当前任务不支持存档确认");
|
||
}
|
||
ApprovalInstance instance = instanceMapper.selectById(task.getInstanceId());
|
||
if (instance == null) {
|
||
throw new BusinessException("单据不存在");
|
||
}
|
||
ensureNotArchived(instance);
|
||
ApprovalNode next = engineService.nextNode(instance, task.getNodeKey());
|
||
List<ApprovalApprover> approvers =
|
||
next == null ? Collections.emptyList() : resolveApprovers(instance, next);
|
||
|
||
String warning = inTransaction(() -> doConfirm(taskId, task, user, next, approvers));
|
||
return new Result(instance.getId(), warning);
|
||
}
|
||
|
||
/**
|
||
* 归档:ARCHIVE 任务或签,处理后取消同节点其余待办,实例进入只读终态。
|
||
*/
|
||
public Result archive(Long taskId, User user) {
|
||
ApprovalTask task = requireTask(taskId, user);
|
||
if (!ApprovalConstants.NODE_ARCHIVE.equals(task.getNodeType())) {
|
||
throw new BusinessException("当前任务不支持归档");
|
||
}
|
||
ApprovalInstance instance = instanceMapper.selectById(task.getInstanceId());
|
||
if (instance == null) {
|
||
throw new BusinessException("单据不存在");
|
||
}
|
||
ensureNotArchived(instance);
|
||
inTransaction(() -> doArchive(taskId, task, user));
|
||
return new Result(instance.getId(), null);
|
||
}
|
||
|
||
/**
|
||
* 驳回:意见必填,同轮待办全部取消,实例回到报送人。
|
||
*/
|
||
public Result reject(Long taskId, String opinion, User user) {
|
||
ApprovalTask task = requireTask(taskId, user);
|
||
boolean rejectable = ApprovalConstants.NODE_APPROVE.equals(task.getNodeType())
|
||
|| ApprovalConstants.NODE_DISPATCH.equals(task.getNodeType());
|
||
if (!rejectable) {
|
||
throw new BusinessException("当前任务不支持驳回");
|
||
}
|
||
String required = requireOpinion(opinion);
|
||
inTransaction(() -> doReject(taskId, task, required, user));
|
||
return new Result(task.getInstanceId(), null);
|
||
}
|
||
|
||
/**
|
||
* 撤回:仅报送人,且本轮没有任何任务被处理。
|
||
*/
|
||
public Result withdraw(Long instanceId, User user) {
|
||
ApprovalInstance instance = instanceMapper.selectById(instanceId);
|
||
if (instance == null) {
|
||
throw new BusinessException("单据不存在");
|
||
}
|
||
if (user == null || !instance.getReporterId().equals(user.getUserId())) {
|
||
throw new BusinessException("无权操作该单据");
|
||
}
|
||
if (ApprovalConstants.STATUS_ARCHIVED.equals(instance.getStatus())) {
|
||
throw new BusinessException("单据已归档,不能修改");
|
||
}
|
||
if (!ApprovalConstants.STATUS_PENDING.equals(instance.getStatus())) {
|
||
throw new BusinessException("当前状态不可撤回");
|
||
}
|
||
ensureNoHandledTask(instance);
|
||
inTransaction(() -> doWithdraw(instanceId, user));
|
||
return new Result(instanceId, null);
|
||
}
|
||
|
||
/**
|
||
* 改派:管理员把待处理任务换给同租户其他启用用户,原因必填并写 ASSIGN 记录。
|
||
*
|
||
* <p>改派不改变任务状态,只替换处理人;操作人不必是任务当前处理人。</p>
|
||
*/
|
||
public ApprovalAssignVO assign(Long taskId, ApprovalTaskAssignParam param, User user) {
|
||
if (param == null || param.getTargetUserId() == null) {
|
||
throw new BusinessException("请选择改派对象");
|
||
}
|
||
String reason = requireAssignReason(param.getReason());
|
||
ApprovalTask task = requireAssignableTask(taskId);
|
||
// 事务外解析目标用户:启用、未删除且属于当前租户(@DS("server") 查询显式带租户)
|
||
List<ApprovalUserVO> targets =
|
||
directoryService.listActiveUsersByIds(Collections.singletonList(param.getTargetUserId()));
|
||
if (targets.isEmpty()) {
|
||
throw new BusinessException("改派对象不存在或已停用");
|
||
}
|
||
ApprovalUserVO target = targets.get(0);
|
||
String targetName = displayName(target);
|
||
return inTransaction(() -> doAssign(taskId, task, target.getUserId(), targetName, reason, user));
|
||
}
|
||
|
||
public Integer todoCount(User user) {
|
||
return taskMapper.countPendingInstances(user.getUserId());
|
||
}
|
||
|
||
private String doApprove(Long taskId, ApprovalTask task, String opinion, User user,
|
||
ApprovalNode next, List<ApprovalApprover> approvers) {
|
||
// 先锁实例串行化同单据的处理,再做任务条件更新,避免并发或签互相等待行锁
|
||
ApprovalInstance locked = engineService.lockInstance(task.getInstanceId());
|
||
ensureTaskHandleable(task, locked);
|
||
if (updatePendingTask(taskId, ApprovalConstants.TASK_DONE, opinion) == 0) {
|
||
throw new BusinessException("任务已处理");
|
||
}
|
||
engineService.cancelPendingTasks(locked.getId(), locked.getRound(), taskId);
|
||
engineService.addRecord(locked, taskId, task.getNodeKey(), user,
|
||
ApprovalConstants.ACTION_APPROVE, opinion);
|
||
if (next == null) {
|
||
return null;
|
||
}
|
||
int created = engineService.moveToNode(locked, next, locked.getRound(), approvers);
|
||
return created == 0 ? "当前节点缺少审批人,请联系管理员补配置" : null;
|
||
}
|
||
|
||
private String doConfirm(Long taskId, ApprovalTask task, User user,
|
||
ApprovalNode next, List<ApprovalApprover> approvers) {
|
||
ApprovalInstance locked = engineService.lockInstance(task.getInstanceId());
|
||
ensureNotArchived(locked);
|
||
ensureTaskHandleable(task, locked);
|
||
if (updatePendingTask(taskId, ApprovalConstants.TASK_DONE, null) == 0) {
|
||
throw new BusinessException("任务已处理");
|
||
}
|
||
engineService.addRecord(locked, taskId, task.getNodeKey(), user,
|
||
ApprovalConstants.ACTION_CONFIRM, null);
|
||
Integer remaining = taskMapper.selectCount(new LambdaQueryWrapper<ApprovalTask>()
|
||
.eq(ApprovalTask::getInstanceId, locked.getId())
|
||
.eq(ApprovalTask::getRound, locked.getRound())
|
||
.eq(ApprovalTask::getNodeKey, task.getNodeKey())
|
||
.eq(ApprovalTask::getStatus, ApprovalConstants.TASK_PENDING));
|
||
if (remaining != null && remaining > 0) {
|
||
return null;
|
||
}
|
||
if (next == null) {
|
||
return "当前节点缺少审批人,请联系管理员补配置";
|
||
}
|
||
int created = engineService.moveToNode(locked, next, locked.getRound(), approvers);
|
||
if (created == 0) {
|
||
return "当前节点缺少审批人,请联系管理员补配置";
|
||
}
|
||
int rows = instanceMapper.update(null, new UpdateWrapper<ApprovalInstance>()
|
||
.eq("id", locked.getId())
|
||
.eq("status", ApprovalConstants.STATUS_PENDING)
|
||
.set("status", ApprovalConstants.STATUS_APPROVED)
|
||
.set("approve_time", LocalDateTime.now()));
|
||
if (rows == 0) {
|
||
throw new BusinessException("单据状态已变化,请刷新后重试");
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private void doArchive(Long taskId, ApprovalTask task, User user) {
|
||
ApprovalInstance locked = engineService.lockInstance(task.getInstanceId());
|
||
ensureNotArchived(locked);
|
||
if (!ApprovalConstants.STATUS_APPROVED.equals(locked.getStatus())) {
|
||
throw new BusinessException("单据状态已变化,请刷新后重试");
|
||
}
|
||
ensureTaskCurrent(task, locked);
|
||
if (updatePendingTask(taskId, ApprovalConstants.TASK_DONE, null) == 0) {
|
||
throw new BusinessException("任务已处理");
|
||
}
|
||
engineService.cancelPendingTasks(locked.getId(), locked.getRound(), taskId);
|
||
engineService.addRecord(locked, taskId, task.getNodeKey(), user,
|
||
ApprovalConstants.ACTION_ARCHIVE, null);
|
||
int rows = instanceMapper.update(null, new UpdateWrapper<ApprovalInstance>()
|
||
.eq("id", locked.getId())
|
||
.eq("status", ApprovalConstants.STATUS_APPROVED)
|
||
.set("status", ApprovalConstants.STATUS_ARCHIVED)
|
||
.set("archive_time", LocalDateTime.now())
|
||
.set("archivist_id", user.getUserId())
|
||
.set("archivist_name", displayName(user)));
|
||
if (rows == 0) {
|
||
throw new BusinessException("单据状态已变化,请刷新后重试");
|
||
}
|
||
}
|
||
|
||
private void doReject(Long taskId, ApprovalTask task, String opinion, User user) {
|
||
ApprovalInstance locked = engineService.lockInstance(task.getInstanceId());
|
||
ensureTaskHandleable(task, locked);
|
||
if (updatePendingTask(taskId, ApprovalConstants.TASK_DONE, opinion) == 0) {
|
||
throw new BusinessException("任务已处理");
|
||
}
|
||
Integer round = locked.getRound();
|
||
engineService.addRecord(locked, taskId, task.getNodeKey(), user,
|
||
ApprovalConstants.ACTION_REJECT, opinion);
|
||
engineService.cancelPendingTasks(locked.getId(), round, null);
|
||
int rows = instanceMapper.update(null, new UpdateWrapper<ApprovalInstance>()
|
||
.eq("id", locked.getId())
|
||
.eq("status", ApprovalConstants.STATUS_PENDING)
|
||
.set("status", ApprovalConstants.STATUS_REJECTED)
|
||
.set("round", round + 1));
|
||
if (rows == 0) {
|
||
throw new BusinessException("单据状态已变化,请刷新后重试");
|
||
}
|
||
}
|
||
|
||
private void doWithdraw(Long instanceId, User user) {
|
||
ApprovalInstance locked = engineService.lockInstance(instanceId);
|
||
if (!ApprovalConstants.STATUS_PENDING.equals(locked.getStatus())) {
|
||
throw new BusinessException("当前状态不可撤回");
|
||
}
|
||
ensureNoHandledTask(locked);
|
||
Integer round = locked.getRound();
|
||
engineService.addRecord(locked, null, locked.getCurrentNodeKey(), user,
|
||
ApprovalConstants.ACTION_WITHDRAW, null);
|
||
engineService.cancelPendingTasks(locked.getId(), round, null);
|
||
int rows = instanceMapper.update(null, new UpdateWrapper<ApprovalInstance>()
|
||
.eq("id", instanceId)
|
||
.eq("status", ApprovalConstants.STATUS_PENDING)
|
||
.set("status", ApprovalConstants.STATUS_WITHDRAWN)
|
||
.set("round", round + 1));
|
||
if (rows == 0) {
|
||
throw new BusinessException("单据状态已变化,请刷新后重试");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 事务内改派:锁实例后条件更新处理人,并追加 ASSIGN 记录。
|
||
*/
|
||
private ApprovalAssignVO doAssign(Long taskId, ApprovalTask task, Integer targetUserId,
|
||
String targetName, String reason, User user) {
|
||
ApprovalInstance locked = engineService.lockInstance(task.getInstanceId());
|
||
ensureTaskHandleable(task, locked);
|
||
int rows = taskMapper.update(null, new UpdateWrapper<ApprovalTask>()
|
||
.eq("id", taskId)
|
||
.eq("status", ApprovalConstants.TASK_PENDING)
|
||
.set("assignee_id", targetUserId)
|
||
.set("assignee_name", targetName));
|
||
if (rows == 0) {
|
||
throw new BusinessException("任务已处理");
|
||
}
|
||
engineService.addRecord(locked, taskId, task.getNodeKey(), user,
|
||
ApprovalConstants.ACTION_ASSIGN, reason);
|
||
ApprovalAssignVO vo = new ApprovalAssignVO();
|
||
vo.setInstanceId(locked.getId());
|
||
vo.setTaskId(taskId);
|
||
vo.setOriginalAssigneeId(task.getAssigneeId());
|
||
vo.setOriginalAssigneeName(task.getAssigneeName());
|
||
vo.setAssigneeId(targetUserId);
|
||
vo.setAssigneeName(targetName);
|
||
vo.setReason(reason);
|
||
return vo;
|
||
}
|
||
|
||
/**
|
||
* 改派前的基本校验:任务存在、待处理且单据仍在审批中。
|
||
*/
|
||
private ApprovalTask requireAssignableTask(Long taskId) {
|
||
if (taskId == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
ApprovalTask task = taskMapper.selectById(taskId);
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
if (!ApprovalConstants.TASK_PENDING.equals(task.getStatus())) {
|
||
throw new BusinessException("任务已处理");
|
||
}
|
||
ApprovalInstance instance = instanceMapper.selectById(task.getInstanceId());
|
||
if (instance == null) {
|
||
throw new BusinessException("单据不存在");
|
||
}
|
||
if (!ApprovalConstants.STATUS_PENDING.equals(instance.getStatus())) {
|
||
throw new BusinessException("单据不在审批中,无法改派");
|
||
}
|
||
return task;
|
||
}
|
||
|
||
/**
|
||
* 任务必须属于实例当前轮次、当前节点,否则视为已被处理或已失效。
|
||
*/
|
||
private void ensureTaskHandleable(ApprovalTask task, ApprovalInstance instance) {
|
||
if (!ApprovalConstants.STATUS_PENDING.equals(instance.getStatus())) {
|
||
throw new BusinessException("单据状态已变化,请刷新后重试");
|
||
}
|
||
ensureTaskCurrent(task, instance);
|
||
}
|
||
|
||
private void ensureTaskCurrent(ApprovalTask task, ApprovalInstance instance) {
|
||
if (!task.getRound().equals(instance.getRound())
|
||
|| !task.getNodeKey().equals(instance.getCurrentNodeKey())) {
|
||
throw new BusinessException("任务已处理");
|
||
}
|
||
}
|
||
|
||
private void ensureNotArchived(ApprovalInstance instance) {
|
||
if (ApprovalConstants.STATUS_ARCHIVED.equals(instance.getStatus())) {
|
||
throw new BusinessException("单据已归档,不能修改");
|
||
}
|
||
}
|
||
|
||
private void ensureNoHandledTask(ApprovalInstance instance) {
|
||
Integer handled = taskMapper.selectCount(new LambdaQueryWrapper<ApprovalTask>()
|
||
.eq(ApprovalTask::getInstanceId, instance.getId())
|
||
.eq(ApprovalTask::getRound, instance.getRound())
|
||
.eq(ApprovalTask::getStatus, ApprovalConstants.TASK_DONE));
|
||
if (handled != null && handled > 0) {
|
||
throw new BusinessException("已有审批人处理,无法撤回");
|
||
}
|
||
}
|
||
|
||
private int updatePendingTask(Long taskId, String status, String opinion) {
|
||
UpdateWrapper<ApprovalTask> wrapper = new UpdateWrapper<ApprovalTask>()
|
||
.eq("id", taskId)
|
||
.eq("status", ApprovalConstants.TASK_PENDING)
|
||
.set("status", status)
|
||
.set("handle_time", LocalDateTime.now());
|
||
if (opinion != null) {
|
||
wrapper.set("opinion", opinion);
|
||
}
|
||
return taskMapper.update(null, wrapper);
|
||
}
|
||
|
||
private ApprovalTask requireTask(Long taskId, User user) {
|
||
if (taskId == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
ApprovalTask task = taskMapper.selectById(taskId);
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
if (user == null || !user.getUserId().equals(task.getAssigneeId())) {
|
||
throw new BusinessException("无权处理该任务");
|
||
}
|
||
return task;
|
||
}
|
||
|
||
/**
|
||
* 事务外按节点解析器解析审批人;缺配时返回空列表,由调用方提示并停留当前节点。
|
||
*/
|
||
private List<ApprovalApprover> resolveApprovers(ApprovalInstance instance, ApprovalNode node) {
|
||
String resolver = node.getResolver();
|
||
if (StrUtil.isBlank(resolver)) {
|
||
return Collections.emptyList();
|
||
}
|
||
switch (resolver) {
|
||
case RESOLVER_REPORTER_ORG_LEADER:
|
||
return roleApprovers(ApprovalConstants.departmentLeaderRoleCode(instance.getReportOrgId()));
|
||
case RESOLVER_ACCEPT_ORG_LEADER:
|
||
return roleApprovers(ApprovalConstants.departmentLeaderRoleCode(instance.getAcceptOrgId()));
|
||
case RESOLVER_ARCHIVIST:
|
||
return roleApprovers(ApprovalConstants.ROLE_ARCHIVIST);
|
||
case RESOLVER_REPORTER:
|
||
return userApprovers(Collections.singletonList(instance.getReporterId()));
|
||
case RESOLVER_REPORTER_AND_DISPATCH_TARGET:
|
||
return userApprovers(confirmCandidateIds(instance));
|
||
case RESOLVER_ACCEPT_ORG_MEMBERS:
|
||
return organizationApprovers(instance.getAcceptOrgId());
|
||
default:
|
||
return Collections.emptyList();
|
||
}
|
||
}
|
||
|
||
private List<Integer> confirmCandidateIds(ApprovalInstance instance) {
|
||
List<Integer> ids = new ArrayList<>();
|
||
if (instance.getReporterId() != null) {
|
||
ids.add(instance.getReporterId());
|
||
}
|
||
if (instance.getDispatchTargetId() != null && !ids.contains(instance.getDispatchTargetId())) {
|
||
ids.add(instance.getDispatchTargetId());
|
||
}
|
||
return ids;
|
||
}
|
||
|
||
private List<ApprovalApprover> roleApprovers(String roleCode) {
|
||
if (StrUtil.isBlank(roleCode)) {
|
||
return Collections.emptyList();
|
||
}
|
||
return toApprovers(directoryService.listRoleMembers(roleCode));
|
||
}
|
||
|
||
private List<ApprovalApprover> userApprovers(List<Integer> userIds) {
|
||
return toApprovers(directoryService.listActiveUsersByIds(userIds));
|
||
}
|
||
|
||
private List<ApprovalApprover> organizationApprovers(Integer organizationId) {
|
||
return toApprovers(directoryService.listOrganizationMembers(organizationId));
|
||
}
|
||
|
||
private List<ApprovalApprover> toApprovers(List<ApprovalUserVO> users) {
|
||
return users.stream()
|
||
.map(user -> new ApprovalApprover(user.getUserId(), displayName(user)))
|
||
.collect(Collectors.toList());
|
||
}
|
||
|
||
private String displayName(ApprovalUserVO user) {
|
||
if (StrUtil.isNotBlank(user.getRealName())) {
|
||
return user.getRealName();
|
||
}
|
||
if (StrUtil.isNotBlank(user.getNickname())) {
|
||
return user.getNickname();
|
||
}
|
||
return user.getUsername();
|
||
}
|
||
|
||
private String displayName(User user) {
|
||
if (StrUtil.isNotBlank(user.getRealName())) {
|
||
return user.getRealName();
|
||
}
|
||
if (StrUtil.isNotBlank(user.getNickname())) {
|
||
return user.getNickname();
|
||
}
|
||
return user.getUsername();
|
||
}
|
||
|
||
private String normalizeOpinion(String opinion) {
|
||
if (opinion == null) {
|
||
return null;
|
||
}
|
||
String trimmed = opinion.trim();
|
||
if (trimmed.isEmpty()) {
|
||
return null;
|
||
}
|
||
if (trimmed.length() > MAX_OPINION_LENGTH) {
|
||
throw new BusinessException("意见不能超过500字");
|
||
}
|
||
return trimmed;
|
||
}
|
||
|
||
private String requireOpinion(String opinion) {
|
||
String normalized = normalizeOpinion(opinion);
|
||
if (normalized == null) {
|
||
throw new BusinessException("请填写驳回意见");
|
||
}
|
||
return normalized;
|
||
}
|
||
|
||
private String requireAssignReason(String reason) {
|
||
String normalized = normalizeOpinion(reason);
|
||
if (normalized == null) {
|
||
throw new BusinessException("请填写改派原因");
|
||
}
|
||
return normalized;
|
||
}
|
||
|
||
private <T> T inTransaction(Supplier<T> action) {
|
||
return new TransactionTemplate(transactionManager).execute(status -> action.get());
|
||
}
|
||
|
||
private void inTransaction(Runnable action) {
|
||
new TransactionTemplate(transactionManager).execute(status -> {
|
||
action.run();
|
||
return null;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 动作结果:实例id + 需要透出的节点缺配提示。
|
||
*/
|
||
public static class Result {
|
||
private final Long instanceId;
|
||
private final String warning;
|
||
|
||
public Result(Long instanceId, String warning) {
|
||
this.instanceId = instanceId;
|
||
this.warning = warning;
|
||
}
|
||
|
||
public Long getInstanceId() {
|
||
return instanceId;
|
||
}
|
||
|
||
public String getWarning() {
|
||
return warning;
|
||
}
|
||
}
|
||
}
|