feat: 完成分享、单会话与发布审批改造

- 增加工作流协作分享与知识库卡片分享入口,统一低版本浏览器复制反馈

- Web 新登录替换旧会话,并保持 API Key 会话隔离

- 发布审批增加必填说明并在审批详情展示

- 账号重置与导入改用可配置默认强密码
This commit is contained in:
2026-07-23 16:09:31 +08:00
parent caa1f07b66
commit 5a42826d44
71 changed files with 3191 additions and 132 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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());

View File

@@ -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());

View File

@@ -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;
}
}