package com.crm.lead.state; import com.crm.lead.constant.LeadConstants; import java.time.LocalDateTime; import java.util.Set; /** * 线索状态迁移命令——sealed 类型体系,每条迁移边一个子类型。 * *

调用方负责预计算所有字段值(快照、deadline 等), * {@link LeadTransition} 不查外部服务,保持对 crm-rule / crm-auth 零依赖。

* *

每条命令自带守卫声明——{@link #allowedFromStatuses()}(合法起始态集合)与 * {@link #requiresOwner()}(是否要求操作人==领取人)。{@link LeadTransition#execute} * 分派前统一读这两项跑一次守卫(ADR-0021 CAS 之上的友好前置)。目标态计算、 * 「起始态×命令内容」组合子规则仍由各 apply 实现负责。

*/ public sealed interface TransitionCmd permits TransitionCmd.ClaimCmd, TransitionCmd.AssignToPoolCmd, TransitionCmd.AssignToUserCmd, TransitionCmd.FeedbackCmd, TransitionCmd.ConvertCmd, TransitionCmd.ReleaseCmd, TransitionCmd.ActivateCmd { /** 操作人 ID——守卫做持有人校验时用。 */ Long operatorId(); /** 这条迁移边的合法起始态集合(守卫层)。 */ Set allowedFromStatuses(); /** 这条边是否要求「操作人 == 领取人」。默认否。 */ default boolean requiresOwner() { return false; } /** 领取:待领取(2) → 已领取(3) */ record ClaimCmd( Long operatorId, String ownerNameSnapshot, Long ownerDeptIdSnapshot, LocalDateTime recycleDeadline ) implements TransitionCmd { @Override public Set allowedFromStatuses() { return Set.of(LeadConstants.STATUS_PENDING); } } /** 分配到公海池:未分发(1) → 待领取(2) */ record AssignToPoolCmd( Long operatorId, Long poolId, String poolNameSnapshot, Long deptId, Long teamDeptId, LocalDateTime expireDeadline ) implements TransitionCmd { @Override public Set allowedFromStatuses() { return Set.of(LeadConstants.STATUS_UNDISTRIBUTED); } } /** * 分配到销售:未分发(1)/待领取(2) → 已领取(3);线索作废(7) → 线索作废(7)(更新 owner)。 * 作废状态下 recycleDeadline 为 null。 */ record AssignToUserCmd( Long operatorId, Long userId, String ownerNameSnapshot, Long ownerDeptIdSnapshot, LocalDateTime recycleDeadline ) implements TransitionCmd { @Override public Set allowedFromStatuses() { return Set.of(LeadConstants.STATUS_UNDISTRIBUTED, LeadConstants.STATUS_PENDING, LeadConstants.STATUS_VOID); } } /** * 提交反馈:已领取(3)/跟进中(4) → 跟进中(4)/线索作废(7);线索作废(7) → 已领取(3)(作废恢复)。 * recycleDeadline 为 null 表示目标状态为线索作废(清 owner,不设 deadline)。 */ record FeedbackCmd( Long operatorId, int feedbackStatus, String content, String productCode, String attachmentIds, LocalDateTime recycleDeadline ) implements TransitionCmd { @Override public Set allowedFromStatuses() { return Set.of(LeadConstants.STATUS_CLAIMED, LeadConstants.STATUS_FOLLOWING, LeadConstants.STATUS_VOID); } @Override public boolean requiresOwner() { return true; } } /** 转商机:已领取(3)/跟进中(4) → 已转商机(5)(outbound port 已被 service 调通) */ record ConvertCmd( Long operatorId, Long opportunityId, String opportunityName ) implements TransitionCmd { @Override public Set allowedFromStatuses() { return Set.of(LeadConstants.STATUS_CLAIMED, LeadConstants.STATUS_FOLLOWING); } @Override public boolean requiresOwner() { return true; } } /** 释放:已领取(3)/跟进中(4) → 待领取(2),清 owner,保留反馈快照 */ record ReleaseCmd( Long operatorId ) implements TransitionCmd { @Override public Set allowedFromStatuses() { return Set.of(LeadConstants.STATUS_CLAIMED, LeadConstants.STATUS_FOLLOWING); } @Override public boolean requiresOwner() { return true; } } /** 激活:过期失效(6) → 恢复失效前态,重置回收计时 N 和失效计时 M */ record ActivateCmd( Long operatorId, LocalDateTime newExpireDeadline, LocalDateTime newRecycleDeadline ) implements TransitionCmd { @Override public Set allowedFromStatuses() { return Set.of(LeadConstants.STATUS_EXPIRED); } } }