feat: 完成分享、单会话与发布审批改造
- 增加工作流协作分享与知识库卡片分享入口,统一低版本浏览器复制反馈 - Web 新登录替换旧会话,并保持 API Key 会话隔离 - 发布审批增加必填说明并在审批详情展示 - 账号重置与导入改用可配置默认强密码
This commit is contained in:
@@ -47,6 +47,12 @@ public class ApprovalInstanceBase implements Serializable {
|
||||
@Column(comment = "审批摘要")
|
||||
private String summary;
|
||||
|
||||
/**
|
||||
* 发起人填写的审批说明。
|
||||
*/
|
||||
@Column(comment = "审批说明")
|
||||
private String applicationReason;
|
||||
|
||||
@Column(comment = "申请人ID")
|
||||
private BigInteger applicantId;
|
||||
|
||||
@@ -148,6 +154,14 @@ public class ApprovalInstanceBase implements Serializable {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public String getApplicationReason() {
|
||||
return applicationReason;
|
||||
}
|
||||
|
||||
public void setApplicationReason(String applicationReason) {
|
||||
this.applicationReason = applicationReason;
|
||||
}
|
||||
|
||||
public BigInteger getApplicantId() {
|
||||
return applicantId;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ public class ApprovalInstanceDetailVo {
|
||||
|
||||
private String summary;
|
||||
|
||||
/**
|
||||
* 发起人填写的审批说明。
|
||||
*/
|
||||
private String applicationReason;
|
||||
|
||||
private BigInteger applicantId;
|
||||
|
||||
private String applicantName;
|
||||
@@ -123,6 +128,14 @@ public class ApprovalInstanceDetailVo {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public String getApplicationReason() {
|
||||
return applicationReason;
|
||||
}
|
||||
|
||||
public void setApplicationReason(String applicationReason) {
|
||||
this.applicationReason = applicationReason;
|
||||
}
|
||||
|
||||
public BigInteger getApplicantId() {
|
||||
return applicantId;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ public class ApprovalInstancePageVo {
|
||||
|
||||
private String summary;
|
||||
|
||||
/**
|
||||
* 发起审批时填写的说明。
|
||||
*/
|
||||
private String applicationReason;
|
||||
|
||||
private BigInteger applicantId;
|
||||
|
||||
private Date submittedAt;
|
||||
@@ -100,6 +105,24 @@ public class ApprovalInstancePageVo {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取发起审批时填写的说明。
|
||||
*
|
||||
* @return 审批说明
|
||||
*/
|
||||
public String getApplicationReason() {
|
||||
return applicationReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置发起审批时填写的说明。
|
||||
*
|
||||
* @param applicationReason 审批说明
|
||||
*/
|
||||
public void setApplicationReason(String applicationReason) {
|
||||
this.applicationReason = applicationReason;
|
||||
}
|
||||
|
||||
public BigInteger getApplicantId() {
|
||||
return applicantId;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ public class ApprovalLogVo {
|
||||
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 提交审批事件对应的申请说明。
|
||||
*/
|
||||
private String applicationReason;
|
||||
|
||||
private BigInteger operatorId;
|
||||
|
||||
private String operatorAccount;
|
||||
@@ -39,6 +44,24 @@ public class ApprovalLogVo {
|
||||
this.eventType = eventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提交审批事件对应的申请说明。
|
||||
*
|
||||
* @return 审批说明,非提交事件时为 {@code null}
|
||||
*/
|
||||
public String getApplicationReason() {
|
||||
return applicationReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置提交审批事件对应的申请说明。
|
||||
*
|
||||
* @param applicationReason 审批说明
|
||||
*/
|
||||
public void setApplicationReason(String applicationReason) {
|
||||
this.applicationReason = applicationReason;
|
||||
}
|
||||
|
||||
public BigInteger getOperatorId() {
|
||||
return operatorId;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ public class ApprovalSubmitRequest {
|
||||
|
||||
private String summary;
|
||||
|
||||
/**
|
||||
* 发起人填写的审批说明。
|
||||
*/
|
||||
private String applicationReason;
|
||||
|
||||
private Map<String, Object> snapshotJson;
|
||||
|
||||
public String getResourceType() {
|
||||
@@ -80,6 +85,24 @@ public class ApprovalSubmitRequest {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批说明。
|
||||
*
|
||||
* @return 审批说明
|
||||
*/
|
||||
public String getApplicationReason() {
|
||||
return applicationReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置审批说明。
|
||||
*
|
||||
* @param applicationReason 审批说明
|
||||
*/
|
||||
public void setApplicationReason(String applicationReason) {
|
||||
this.applicationReason = applicationReason;
|
||||
}
|
||||
|
||||
public Map<String, Object> getSnapshotJson() {
|
||||
return snapshotJson;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ public class ApprovalTaskVo {
|
||||
|
||||
private String stepName;
|
||||
|
||||
/**
|
||||
* 当前审批实例的申请说明。
|
||||
*/
|
||||
private String applicationReason;
|
||||
|
||||
private String status;
|
||||
|
||||
private String assigneeRoleCode;
|
||||
@@ -58,6 +63,24 @@ public class ApprovalTaskVo {
|
||||
this.stepName = stepName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前审批实例的申请说明。
|
||||
*
|
||||
* @return 审批说明
|
||||
*/
|
||||
public String getApplicationReason() {
|
||||
return applicationReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前审批实例的申请说明。
|
||||
*
|
||||
* @param applicationReason 审批说明
|
||||
*/
|
||||
public void setApplicationReason(String applicationReason) {
|
||||
this.applicationReason = applicationReason;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ public class ApprovalInstanceServiceImpl implements ApprovalInstanceService {
|
||||
instance.setCurrentStepNo(firstStep.getStepNo());
|
||||
instance.setSnapshotJson(buildInstanceSnapshot(request, flow, steps));
|
||||
instance.setSummary(request.getSummary());
|
||||
instance.setApplicationReason(request.getApplicationReason());
|
||||
instance.setApplicantId(request.getApplicantId());
|
||||
instance.setSubmittedAt(now);
|
||||
instance.setCreated(now);
|
||||
@@ -102,11 +103,15 @@ public class ApprovalInstanceServiceImpl implements ApprovalInstanceService {
|
||||
approvalInstanceMapper.insert(instance);
|
||||
|
||||
createTask(instance.getId(), firstStep, request.getApplicantId(), now);
|
||||
appendLog(instance.getId(), ApprovalEventType.SUBMITTED.getCode(), request.getApplicantId(), Map.of(
|
||||
"flowId", flow.getId(),
|
||||
"flowVersion", flow.getVersion(),
|
||||
"summary", request.getSummary()
|
||||
), now);
|
||||
Map<String, Object> submittedPayload = new LinkedHashMap<>();
|
||||
submittedPayload.put("flowId", flow.getId());
|
||||
submittedPayload.put("flowVersion", flow.getVersion());
|
||||
submittedPayload.put("summary", request.getSummary());
|
||||
if (request.getApplicationReason() != null) {
|
||||
submittedPayload.put("applicationReason", request.getApplicationReason());
|
||||
}
|
||||
appendLog(instance.getId(), ApprovalEventType.SUBMITTED.getCode(), request.getApplicantId(),
|
||||
submittedPayload, now);
|
||||
appendLog(instance.getId(), ApprovalEventType.STEP_CREATED.getCode(), request.getApplicantId(),
|
||||
buildStepCreatedPayload(firstStep), now);
|
||||
return instance.getId();
|
||||
@@ -228,6 +233,9 @@ public class ApprovalInstanceServiceImpl implements ApprovalInstanceService {
|
||||
snapshot.put("deptId", request.getDeptId());
|
||||
snapshot.put("flowId", flow.getId());
|
||||
snapshot.put("flowVersion", flow.getVersion());
|
||||
if (request.getApplicationReason() != null) {
|
||||
snapshot.put("applicationReason", request.getApplicationReason());
|
||||
}
|
||||
snapshot.put("steps", steps.stream().map(item -> {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("stepNo", item.getStepNo());
|
||||
|
||||
@@ -18,6 +18,7 @@ import tech.easyflow.approval.entity.vo.ApprovalFlowStepVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalLogVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalTaskVo;
|
||||
import tech.easyflow.approval.enums.ApprovalActionType;
|
||||
import tech.easyflow.approval.enums.ApprovalEventType;
|
||||
import tech.easyflow.approval.enums.ApprovalInstanceStatus;
|
||||
import tech.easyflow.approval.enums.ApprovalResourceType;
|
||||
import tech.easyflow.approval.enums.ApprovalTaskStatus;
|
||||
@@ -144,6 +145,7 @@ public class ApprovalQueryServiceImpl implements ApprovalQueryService {
|
||||
detail.setStatus(instance.getStatus());
|
||||
detail.setCurrentStepNo(instance.getCurrentStepNo());
|
||||
detail.setSummary(instance.getSummary());
|
||||
detail.setApplicationReason(instance.getApplicationReason());
|
||||
detail.setApplicantId(instance.getApplicantId());
|
||||
detail.setSubmittedAt(instance.getSubmittedAt());
|
||||
detail.setFinishedAt(instance.getFinishedAt());
|
||||
@@ -165,6 +167,7 @@ public class ApprovalQueryServiceImpl implements ApprovalQueryService {
|
||||
taskVo.setId(item.getId());
|
||||
taskVo.setStepNo(item.getStepNo());
|
||||
taskVo.setStepName(resolveStepName(frozenStepMap, item.getStepNo()));
|
||||
taskVo.setApplicationReason(instance.getApplicationReason());
|
||||
taskVo.setStatus(item.getStatus());
|
||||
taskVo.setAssigneeRoleCode(item.getAssigneeRoleCode());
|
||||
taskVo.setAssigneeType(item.getAssigneeType());
|
||||
@@ -185,6 +188,9 @@ public class ApprovalQueryServiceImpl implements ApprovalQueryService {
|
||||
ApprovalLogVo logVo = new ApprovalLogVo();
|
||||
logVo.setId(item.getId());
|
||||
logVo.setEventType(item.getEventType());
|
||||
if (ApprovalEventType.SUBMITTED.getCode().equals(item.getEventType())) {
|
||||
logVo.setApplicationReason(instance.getApplicationReason());
|
||||
}
|
||||
logVo.setOperatorId(item.getOperatorId());
|
||||
logVo.setOperatorAccount(resolveAccountLoginName(accountMap.get(item.getOperatorId())));
|
||||
logVo.setOperatorName(resolveAccountName(accountMap.get(item.getOperatorId())));
|
||||
@@ -305,6 +311,7 @@ public class ApprovalQueryServiceImpl implements ApprovalQueryService {
|
||||
item.setCurrentStepNo(record.getCurrentStepNo());
|
||||
item.setCurrentStepName(resolveCurrentStepName(record));
|
||||
item.setSummary(record.getSummary());
|
||||
item.setApplicationReason(record.getApplicationReason());
|
||||
item.setApplicantId(record.getApplicantId());
|
||||
item.setSubmittedAt(record.getSubmittedAt());
|
||||
item.setFinishedAt(record.getFinishedAt());
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package tech.easyflow.approval.support;
|
||||
|
||||
import tech.easyflow.approval.enums.ApprovalActionType;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
/**
|
||||
* 发布审批说明校验策略。
|
||||
*/
|
||||
public final class ApprovalApplicationReasonPolicy {
|
||||
|
||||
/**
|
||||
* 审批说明最大长度。
|
||||
*/
|
||||
public static final int MAX_LENGTH = 500;
|
||||
|
||||
private ApprovalApplicationReasonPolicy() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按审批匹配结果和动作类型规范化审批说明。
|
||||
*
|
||||
* @param approvalRequired 是否命中审批流
|
||||
* @param actionType 动作类型
|
||||
* @param applicationReason 原始审批说明
|
||||
* @return 需要说明时返回去除首尾空白的文本,否则返回 {@code null}
|
||||
* @throws BusinessException 必填说明为空或超过长度限制时抛出
|
||||
*/
|
||||
public static String normalize(
|
||||
boolean approvalRequired,
|
||||
String actionType,
|
||||
String applicationReason
|
||||
) {
|
||||
if (!approvalRequired || !ApprovalActionType.PUBLISH.getCode().equals(actionType)) {
|
||||
return null;
|
||||
}
|
||||
String normalized = applicationReason == null ? "" : applicationReason.trim();
|
||||
if (normalized.isEmpty()) {
|
||||
throw new BusinessException(400, 400, "请填写审批说明");
|
||||
}
|
||||
if (normalized.length() > MAX_LENGTH) {
|
||||
throw new BusinessException(400, 400, "审批说明不能超过500字");
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package tech.easyflow.approval.support;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import tech.easyflow.approval.enums.ApprovalActionType;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
/**
|
||||
* {@link ApprovalApplicationReasonPolicy} 测试。
|
||||
*/
|
||||
public class ApprovalApplicationReasonPolicyTest {
|
||||
|
||||
/**
|
||||
* 验证命中发布审批时说明必填并去除首尾空白。
|
||||
*/
|
||||
@Test
|
||||
public void shouldNormalizeRequiredPublishReason() {
|
||||
String result = ApprovalApplicationReasonPolicy.normalize(
|
||||
true,
|
||||
ApprovalActionType.PUBLISH.getCode(),
|
||||
" 修复校验逻辑并补充测试 "
|
||||
);
|
||||
|
||||
Assert.assertEquals("修复校验逻辑并补充测试", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证命中发布审批时拒绝空说明。
|
||||
*/
|
||||
@Test(expected = BusinessException.class)
|
||||
public void shouldRejectBlankRequiredPublishReason() {
|
||||
ApprovalApplicationReasonPolicy.normalize(
|
||||
true,
|
||||
ApprovalActionType.PUBLISH.getCode(),
|
||||
" "
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证命中发布审批时拒绝超过 500 字的说明。
|
||||
*/
|
||||
@Test(expected = BusinessException.class)
|
||||
public void shouldRejectOversizedRequiredPublishReason() {
|
||||
ApprovalApplicationReasonPolicy.normalize(
|
||||
true,
|
||||
ApprovalActionType.PUBLISH.getCode(),
|
||||
"a".repeat(501)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证未命中审批或非发布动作无需说明。
|
||||
*/
|
||||
@Test
|
||||
public void shouldIgnoreReasonWhenItIsNotRequired() {
|
||||
Assert.assertNull(ApprovalApplicationReasonPolicy.normalize(
|
||||
false,
|
||||
ApprovalActionType.PUBLISH.getCode(),
|
||||
null
|
||||
));
|
||||
Assert.assertNull(ApprovalApplicationReasonPolicy.normalize(
|
||||
true,
|
||||
ApprovalActionType.OFFLINE.getCode(),
|
||||
null
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user