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