Carry actual flow state and freeze callback round identity

Local review candidate only. Explicitly selected inherited prerequisites and prior repair patches; unrelated WIP and independent MQ fixes are excluded. No push or deployment authorized.
This commit is contained in:
chenxing
2026-10-23 10:52:00 +08:00
parent 7eeaea55f8
commit f7819b14e2
13 changed files with 534 additions and 3 deletions
@@ -120,6 +120,10 @@ public class CallbackDispatcher {
if (event.getData() != null) {
data.putAll(event.getData());
}
// 身份在事件发布线程中冻结,不能用 AFTER_COMMIT 重读到的新轮给旧事件重新贴标。
if (event.getRoundIdentity() != null) {
data.putAll(event.getRoundIdentity());
}
payload.put("data", data);
String payloadJson = objectMapper.writeValueAsString(payload);
@@ -0,0 +1,23 @@
package com.yunhai.flowcenter.callback;
import com.yunhai.flowcenter.engine.FlowEvent;
import com.yunhai.flowcenter.repository.FlwInstanceRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/** 同步发布阶段冻结轮次身份;不修改引擎或 apply/v2 七支分支。 */
@Component
@RequiredArgsConstructor
public class FlowEventIdentityListener {
private final FlwInstanceRepository instances;
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE)
public void capture(FlowEvent event) {
instances.findById(event.getInstanceId()).ifPresent(instance ->
event.captureRoundIdentity(instance.getApplyRound(), instance.getApplyRequestToken()));
}
}
@@ -69,6 +69,15 @@ public class FlowEvent {
private final String operatorName;
/** 事件明细(result/opinion/rejectNode/task.created 的 n/m 等),可为空 */
private final Map<String, Object> data;
private Map<String, Object> roundIdentity;
public void captureRoundIdentity(Integer round, String requestToken) {
if (roundIdentity != null) return;
Map<String, Object> identity = new HashMap<>();
identity.put("applyRound", round);
identity.put("requestToken", requestToken);
roundIdentity = java.util.Collections.unmodifiableMap(identity);
}
public static FlowEvent of(Type type, Long instanceId) {
return new FlowEvent(type, instanceId, null, null, null, new HashMap<>());
@@ -60,6 +60,9 @@ public final class FlowApplyRetryModels {
private String branch;
/** 行上的当前轮次(消费方据此校正自己的命令表认知) */
private Integer applyRound;
/** 同一响应的真实状态/实例版本;分支判据保持不变。 */
private Integer status;
private Integer version;
}
/** 分支码常量(与 {@link ApplyResultV2#branch} 同值域) */
@@ -107,6 +107,8 @@ public final class OapiModels {
@Data
public static class InstanceVo {
private Long instanceId;
private Integer applyRound;
private String requestToken;
private Integer status;
private Integer version;
private String nodeSummary;
@@ -223,6 +223,15 @@ public class OapiFlowService {
vo.setDuplicated(outcome.duplicated);
vo.setBranch(outcome.branch);
vo.setApplyRound(outcome.applyRound);
instanceRepository.findById(outcome.instanceId).ifPresent(current -> {
int currentRound = current.getApplyRound() == null ? 1 : current.getApplyRound();
int outcomeRound = outcome.applyRound == null ? 1 : outcome.applyRound;
if (currentRound == outcomeRound) {
vo.setStatus(current.getStatus());
vo.setVersion(current.getVersion());
} // 已被另一请求推进到新轮时,让消费方按实例查询恢复,不混装两轮证据。
});
return vo;
}
@@ -252,6 +261,8 @@ public class OapiFlowService {
vo.setInstanceId(existing.getId());
vo.setDuplicated(true);
vo.setApplyRound(existing.getApplyRound());
vo.setStatus(existing.getStatus());
vo.setVersion(existing.getVersion());
vo.setBranch(token != null && token.equals(existing.getApplyRequestToken())
? FlowApplyRetryModels.Branch.SAME_REQUEST : FlowApplyRetryModels.Branch.CONCURRENT_CREATED);
log.warn("[flow-center] 并发建件命中既有实例:instance={} biz={}/{} branch={}",
@@ -1112,6 +1123,8 @@ public class OapiFlowService {
public OapiModels.InstanceVo toInstanceVo(FlwInstance instance) {
OapiModels.InstanceVo vo = new OapiModels.InstanceVo();
vo.setInstanceId(instance.getId());
vo.setApplyRound(instance.getApplyRound());
vo.setRequestToken(instance.getApplyRequestToken());
vo.setStatus(instance.getStatus());
vo.setVersion(instance.getVersion());
vo.setBizTitle(instance.getBizTitle());