package com.yunhai.flowcenter.callback; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.yunhai.flowcenter.entity.FlwCallbackReg; import com.yunhai.flowcenter.entity.FlwInstance; import com.yunhai.flowcenter.repository.FlwInstanceRepository; import com.yunhai.flowcenter.service.FlowSourcePrincipalResolver; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; /** Same SELF predicate for live fanout, fact recovery, retries and manual re-delivery. */ @Component @RequiredArgsConstructor public class CallbackSourcePolicy { private final FlowSourcePrincipalResolver resolver; private final FlwInstanceRepository instances; private final ObjectMapper mapper; public boolean matches(FlwCallbackReg reg, FlwInstance instance) { if (instance.getSourcePrincipalId() == null) { return true; // Legacy callback migration is explicit; formal facts always use UUID SELF. } if (!instance.getSourcePrincipalId().equals(reg.getSourcePrincipalId())) return false; // Recheck the credential mapping: a reused appKey cannot receive its predecessor's facts. return instance.getSourcePrincipalId().equals(resolver.resolve(reg.getAppKey()).id); } public boolean permits(FlwCallbackReg reg, com.yunhai.flowcenter.entity.FlwCallbackLog delivery) { if (!permits(reg, delivery.getInstanceId(), delivery.getPayloadJson())) return false; FlwInstance instance = instances.findById(delivery.getInstanceId()).orElseThrow(IllegalStateException::new); if (instance.getSourcePrincipalId() == null) return true; try { JsonNode body = mapper.readTree(delivery.getPayloadJson()); return java.util.Objects.equals(delivery.getEventId(), body.path("eventId").asText()) && java.util.Objects.equals(delivery.getEvent(), body.path("event").asText()) && java.util.Objects.equals(delivery.getBizType(), body.path("bizType").asText()) && java.util.Objects.equals(delivery.getBizId(), body.path("bizId").asText()); } catch (Exception e) { return false; } } public boolean permits(FlwCallbackReg reg, Long instanceId, String payload) { FlwInstance instance = instances.findById(instanceId).orElse(null); if (instance == null || !matches(reg, instance)) return false; if (instance.getSourcePrincipalId() == null) return true; try { JsonNode body = mapper.readTree(payload); JsonNode data = body.path("data"); return instance.getSourcePrincipalId().equals(body.path("sourcePrincipalId").asText()) && instance.getApplyRound().equals(body.path("applyRound").asInt()) && instance.getSourcePrincipalId().equals(data.path("sourcePrincipalId").asText()) && instance.getApplyRequestToken().equals(data.path("requestId").asText()) && instance.getApplyPayloadHash().equals(data.path("canonicalHash").asText()) && instance.getDefVersion().equals(data.path("defVersion").asInt()) && instance.getApplyRound().equals(data.path("applyRound").asInt()) && instance.getId().equals(body.path("instanceId").asLong()) && instance.getSchId().equals(body.path("schId").asLong()) && instance.getBizType().equals(body.path("bizType").asText()) && instance.getBizId().equals(body.path("bizId").asText()); } catch (Exception e) { return false; } } }