Skip to content
Merged

Dev #225

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,19 @@ public float getMultiOperatorAuditMergePercent() {
return 0;
}

/**
* 提交人与审批人一致时是否自动审批
*
* <p>对应 {@link SameOperatorAuditStrategy.Type#AUTO_PASS}:
* 当前节点的审批人为流程提交人本人时,自动通过该节点(无需本人审批,issue #224)。
*
* @return true 表示提交人与审批人相同时自动通过当前节点
*/
public boolean isSameOperatorAutoPass() {
SameOperatorAuditStrategy strategy = getStrategy(SameOperatorAuditStrategy.class);
return strategy != null && strategy.getType() == SameOperatorAuditStrategy.Type.AUTO_PASS;
}

public void verifyNode(FlowForm form) {
for (INodeStrategy strategy : strategies) {
strategy.verifyNode(form);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ public List<FlowTodoMerge> findByTodoId(long todoId) {
.toList();
}

@Override
public List<FlowTodoMerge> findByTodoIds(List<Long> todoIds) {
return cache.values().stream().
filter(relation -> todoIds.contains(relation.getTodoId()))
.toList();
}


public List<FlowTodoMerge> findAll() {
return cache.values().stream().toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,22 @@ private List<FlowRecord> generateCurrentRecords(FlowSession session, Set<String>
}
}
List<IFlowOperator> operators = operatorManager.getOperators();
// 提交人与审批人一致时自动审批(issue #224):提交人流转到该节点且审批人含提交人本人时,
// 过滤掉与提交人一致的操作员,避免本人审批本人提交的节点(AUTO_PASS 自动通过 / MANUAL_PASS 不跳过)。
// 守卫条件(currentOperator == submitOperator)用于区分正常流转与加签等"为他人新增记录"的调用路径:
// 加签(AddAuditAction)通过 updateSession(加签目标) 构造会话,currentOperator 为被加签人而非提交人。
if (nodeStrategyManager.isSameOperatorAutoPass()
&& session.getCurrentOperator() != null
&& session.getCurrentOperator().getUserId() == session.getSubmitOperatorId()) {
long submitOperatorId = session.getSubmitOperatorId();
operators = operators.stream()
.filter(operator -> operator.getUserId() != submitOperatorId)
.toList();
// 全部审批人均与提交人一致,当前节点自动通过,继续向后续节点生成记录
if (operators.isEmpty()) {
return this.generateNextNodeRecords(session);
}
}
for (int order = 0; order < operators.size(); order++) {
IFlowOperator operator = operators.get(order);
FlowRecord flowRecord = new FlowRecord(session.updateSession(operator), order);
Expand Down Expand Up @@ -213,6 +229,34 @@ private List<FlowRecord> generateCurrentRecords(FlowSession session, Set<String>
}


/**
* 当前节点自动通过(如审批人均为提交人本人且配置相同人员自动审批),
* 继续向后续节点生成流程记录。
*
* <p>与 {@link com.codingapi.flow.action.BaseAction#triggerNode} 的节点遍历语义一致:
* 控制节点(条件/并行等)递归深入,业务节点直接生成记录(issue #224)。
*
* @param session 当前会话(currentNode 为自动通过的节点)
* @return 后续节点生成的流程记录,可能为空
*/
private List<FlowRecord> generateNextNodeRecords(FlowSession session) {
List<IFlowNode> nextNodes = session.matchNextNodes();
if (nextNodes == null || nextNodes.isEmpty()) {
return new ArrayList<>();
}
List<FlowRecord> records = new ArrayList<>();
for (IFlowNode node : nextNodes) {
FlowSession nextSession = session.updateSession(node);
if (node.handle(nextSession)) {
records.addAll(this.generateNextNodeRecords(nextSession));
} else {
records.addAll(node.generateCurrentRecords(nextSession));
}
}
return records;
}


@SneakyThrows
public static <T extends BaseAuditNode> T formMap(Map<String, Object> map, Class<T> clazz) {
T node = BaseFlowNode.fromMap(map, clazz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public abstract class BaseFlowNode implements IFlowNode {
/**
* 节点策略
*
* <p>节点默认持有完整策略集(如 {@link ApprovalNode} 的默认集),自定义/反序列化传入的策略按类型合并:
* 类型已存在则覆盖其配置,类型不存在则追加保留——保证显式配置的策略(如
* {@link com.codingapi.flow.strategy.node.SameOperatorAuditStrategy},未注册为默认)能真正生效(配置驱动)。
*
* @param strategies 节点策略
*/
public void setStrategies(List<INodeStrategy> strategies) {
Expand All @@ -87,6 +91,8 @@ public void setStrategies(List<INodeStrategy> strategies) {
INodeStrategy currentStrategy = nodeStrategyManager.getStrategy(nodeStrategy.getClass());
if (currentStrategy != null) {
currentStrategy.copy(nodeStrategy);
} else {
this.strategies.add(nodeStrategy);
}
}
}else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ private static List<INodeStrategy> defaultStrategies() {
List<INodeStrategy> strategies = new ArrayList<>();
strategies.add(TimeoutStrategy.defaultStrategy());
strategies.add(MultiOperatorAuditStrategy.defaultStrategy());
strategies.add(SameOperatorAuditStrategy.defaultStrategy());
// 相同人员审批(SameOperatorAuditStrategy)不设默认:配置驱动,显式配置才生效(issue #224)。
// 若默认 AUTO_PASS 会令所有"提交人==审批人"节点被自动跳过,改变未配置流程的传统行为。
strategies.add(RecordMergeStrategy.defaultStrategy());
strategies.add(ResubmitStrategy.defaultStrategy());
strategies.add(AdviceStrategy.defaultStrategy());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ private static List<INodeStrategy> defaultStrategies() {
List<INodeStrategy> strategies = new ArrayList<>();
strategies.add(TimeoutStrategy.defaultStrategy());
strategies.add(MultiOperatorAuditStrategy.defaultStrategy());
strategies.add(SameOperatorAuditStrategy.defaultStrategy());
// 相同人员审批(SameOperatorAuditStrategy)不设默认:配置驱动,显式配置才生效(issue #224)。
// 若默认 AUTO_PASS 会令所有"提交人==办理人"节点被自动跳过,改变未配置流程的传统行为。
strategies.add(RecordMergeStrategy.defaultStrategy());
strategies.add(ResubmitStrategy.defaultStrategy());
strategies.add(AdviceStrategy.defaultStrategy());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ public void pushRecords(FlowRecord record, List<FlowRecord> mergeRecords) {
body.setNodeName(item.getNodeName());
body.setNodeType(item.getNodeType());
body.setSubmitOperator(new FlowOperator(item.getSubmitOperatorId(), item.getSubmitOperatorName()));
body.setCreatedOperator(new FlowOperator(record.getCreateOperatorId(), record.getCreateOperatorName()));
body.setCreatedOperator(new FlowOperator(item.getCreateOperatorId(), item.getCreateOperatorName()));
body.setTitle(item.getTitle());
body.setData(item.getFormData());
body.setRecordState(item.getRecordState());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ public interface FlowTodoMergeRepository {

List<FlowTodoMerge> findByTodoId(long todoId);

/**
* 按待办id批量查询合并关系
* @param todoIds 待办id列表
* @return 合并关系列表
*/
List<FlowTodoMerge> findByTodoIds(List<Long> todoIds);

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
* 流程记录保存服务,负责保存流程记录和待办记录的合并关系
Expand Down Expand Up @@ -65,17 +67,43 @@ private void saveTodoMargeRecords() {
}
}

// 幂等登记:预加载已存在待办的合并关系,用于判断某条流程记录是否已登记过,
// 避免已读/详情等场景重复保存时反复新增合并关系、膨胀合并计数(issue #223)
List<Long> existedTodoIds = existedByKey.values().stream()
.map(FlowTodoRecord::getId)
.toList();
Map<Long, Set<Long>> recordIdsByTodoId = new HashMap<>();
if (!existedTodoIds.isEmpty()) {
for (FlowTodoMerge relation : flowTodoMergeRepository.findByTodoIds(existedTodoIds)) {
recordIdsByTodoId.computeIfAbsent(relation.getTodoId(), k -> new HashSet<>())
.add(relation.getRecordId());
}
}

// 需要新增的合并关系:todoKey -> 流程记录id(新建待办或该流程记录首次登记时产生)
Map<String, List<Long>> relationCandidates = new HashMap<>();

List<FlowTodoRecord> flowTodoRecords = new ArrayList<>();
for (FlowRecord flowRecord : flowRecords) {
if (flowRecord.isTodo()) {
FlowTodoRecord todoMargeRecord = existedByKey.get(flowRecord.getTodoKey());
if (todoMargeRecord == null) {
todoMargeRecord = new FlowTodoRecord(flowRecord);
existedByKey.put(flowRecord.getTodoKey(), todoMargeRecord);
if (flowRecord.isMergeable()) {
relationCandidates
.computeIfAbsent(flowRecord.getTodoKey(), k -> new ArrayList<>())
.add(flowRecord.getId());
}
} else {
todoMargeRecord.update(flowRecord);
if (flowRecord.isMergeable()) {
if (flowRecord.isMergeable()
&& !recordIdsByTodoId.getOrDefault(todoMargeRecord.getId(), Set.of())
.contains(flowRecord.getId())) {
todoMargeRecord.addMergeCount();
relationCandidates
.computeIfAbsent(flowRecord.getTodoKey(), k -> new ArrayList<>())
.add(flowRecord.getId());
}
}
flowTodoRecords.add(todoMargeRecord);
Expand All @@ -85,11 +113,13 @@ private void saveTodoMargeRecords() {
flowTodoRecordRepository.saveAll(flowTodoRecords);
}

if (!flowTodoRecords.isEmpty()) {
if (!relationCandidates.isEmpty()) {
List<FlowTodoMerge> relationList = new ArrayList<>();
for (FlowTodoRecord margeRecord : flowTodoRecords) {
if (margeRecord.isMergeable()) {
relationList.add(new FlowTodoMerge(margeRecord));
for (Map.Entry<String, List<Long>> entry : relationCandidates.entrySet()) {
FlowTodoRecord todoMargeRecord = existedByKey.get(entry.getKey());
for (long recordId : entry.getValue()) {
relationList.add(new FlowTodoMerge(0L, todoMargeRecord.getId(), recordId,
todoMargeRecord.getCreateTime()));
}
}
flowTodoMergeRepository.saveAll(relationList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public List<FlowTodoMerge> findByTodoId(long todoId) {
.toList();
}

@Override
public List<FlowTodoMerge> findByTodoIds(List<Long> todoIds) {
return cache.values().stream().
filter(relation -> todoIds.contains(relation.getTodoId()))
.toList();
}


public List<FlowTodoMerge> findAll() {
return cache.values().stream().toList();
Expand Down
Loading
Loading