제품 출시를 위한 새로운 비즈니스 상태 머신에 액세스하는 실습

1. 신제품 출시사업 소개

001.png

제품 목록은 Dewu 플랫폼에 신제품을 올리는 것을 의미합니다. 완전한 제품 목록 프로세스는 다양한 소스와 채널에서 신제품 신청서를 제출하는 것으로 시작되며 주로 다음을 포함하여 다양한 역할에 따라 여러 차례의 검토를 거쳐야 합니다.

  • 제품 선택 검토: 신제품 신청서에 제출된 정보를 바탕으로 진열 요구 사항을 충족하는지 여부를 결정합니다.

  • 제품 데이터 검토: 여러 차례의 비즈니스 관리, 위험 제어 및 법적 검토를 포함하여 정확성과 완전성을 위해 제품 데이터를 검토합니다.

  • 비즈니스 리서치 리뷰: 비즈니스 리서치 리뷰는 제품이 플랫폼에서 식별하고 지원할 수 있는 능력에 대한 판단이며, 이는 Dewu 비즈니스의 특징이기도 합니다.

이러한 검토에서는 제품 선택 검토 및 비즈니스 조사 검토가 신제품 샘플링 프로세스에 속하며 제품 데이터 검토에서 해당 제품이 판매될 수 있는지 여부를 결정합니다. 상품 데이터 처리 프로세스에 속하며, 현재 상품 정보가 C측에 표시되기 위한 요구 사항을 충족하는지 여부를 판단합니다.

따라서 시스템 구현에는 신제품 입고 샘플 프로세스의 상태 흐름과 제품 데이터 처리 프로세스가 포함되어야 합니다. 전자는 신제품 입고 샘플 테이블을 포함하고 후자는 주로 제품 SPU 메인 테이블입니다. 신제품 샘플링 프로세스의 소스 채널 속성인 액세스는 매우 명확하며 다양한 채널의 비즈니스 논리 및 프로세스에는 크고 작은 차이가 있습니다.

2. 상태 머신에 대한 액세스를 고려하는 이유는 무엇입니까?

  • 상태 열거 값은 많고 전송 조건이 불분명합니다. 비즈니스 프로세스를 이해하려면 코드를 주의 깊게 연구해야 하며 코드를 시작하고 유지하는 데 드는 비용이 높습니다.

  • 상태의 이전은 완전히 코드에 의해 임의로 지정되며 상태 간 임의 이전에는 위험이 있습니다.

  • 일부 상태 전송은 멱등성을 지원하지 않으며 반복 작업으로 인해 예상치 못한 결과가 발생할 수 있습니다.

  • 새로운 상태와 수정된 상태를 전송하는 데 드는 비용과 위험은 높고, 코드 수정 범위는 통제할 수 없으며, 테스트에는 전체 프로세스의 회귀가 필요합니다.

3. 신제품 출시 프로세스 관련 현황

신제품 샘플 현황 열거

신제품 샘플 테이블에 해당하는 상태 필드에는 다음 열거 값이 포함됩니다(설명의 편의를 위해 적당히 단순화됨).

public enum NewProductShowEnum {
    DRAFT(0, "草稿"),
    CHECKING(1, "选品中"),
    UNPUT_ON_SALE_UNPASS(2, "选品不通过"),
    UNPUT_ON_SALE_PASSED(3, "商研审核中"),
    UNPUT_ON_SALE_PASSED_UNSEND(4, "商品资料待审核"),
    UNPUT_ON_SALE_PASSED_UNSEND_NOT_PUT(5, "鉴别不通过"),
    UNPUT_ON_SALE_PASSED_SEND(6, "请寄样"),
    SEND_PRODUCT(7, "商品已寄样"),
    SEND_PASS(8, "寄样鉴别通过"),
    SEND_REJECT(9, "寄样鉴别不通过"),
    GONDOR_INVALID(10, "作废"),
    FINSH_SPU(11, "新品资料审核通过"),
}

SPU 상태 열거

제품 SPU 메인 테이블에 해당하는 상태 필드에는 다음 열거 값이 포함됩니다(설명의 편의를 위해 적당히 단순화됨).

public enum SpuStatusEnum {
    OFF_SHELF(0, "下架"),
    ON_SHELF(1, "上架"),
    TO_APPROVE(2, "待审核"),
    APPROVED(3, "审核通过"),
    REJECT(4, "审核不通过"),
    TO_RISK_APPROVE(8, "待风控审核"),
    TO_LEGAL_APPROVE(9, "待法务审核"),
}

신제품 출시 비즈니스 프로세스에 관여하는 SPU의 상태 전송 부분은 제품의 상태 전송에도 적용됩니다. (그러나 이 기사에서 논의되는 내용은 아닙니다.) 기사에서는 주로 신제품 샘플 테이블의 상태에 대해 설명합니다.

4. 신제품 샘플 관련 모든 이벤트

  • 새 제품 초안 저장

  • 신제품 신청서 제출

  • 제품 선택 통과

  • 제품 선택 실패

  • 제품 선택이 거부된 후 다시 제출

  • 사업 연구 검토 시작

  • 사업연구 검토-지원 식별

  • 비즈니스 연구 검토-식별은 지원되지 않습니다.

  • 비즈니스 연구 검토 - 제품 정보가 올바르지 않습니다.

  • X일 이상 SPU 검토가 거부되었습니다.

  • 샘플 전송 시작

  • 샘플 배송 진행상황 업데이트

총 12개.

5. 신제품 샘플 상태 이전

위에서 언급했듯이, 제품 소스 채널마다 신제품 출시 프로세스가 다르며, 이는 채널별로 상태 흐름도 다르다는 것을 의미합니다. 다음은 B측 판매자 채널을 보여줍니다.

002.png

그림에서 주황색 박스는 신제품 입고 샘플의 상태, 녹색 박스는 SPU 상태, 파란색 둥근 박스는 상태 변화를 촉발하는 이벤트를 나타내고, 화살표로 연결된 곳은 현재 상태로부터의 흐름을 나타낸다. 다음 상태로.

특정 이벤트가 발생하면 전환해야 할 대상 상태가 고정되어 있지 않으며 이벤트가 최종적으로 흐를 대상 상태를 결정하려면 일련의 논리적 판단이 필요합니다.

6. 상태머신 기술 선정

실제 상태 머신 프레임워크로 Spring StateMachine을 선택하세요. 특정 프로세스 및 세부 정보는 이 문서( https://mp.weixin.qq.com/s/TqXMtS44D4w6d1-KLxcoiQ )를 참조하세요 .

7. 상태 머신 액세스가 직면한 어려움

현재 신제품 샘플용 코드는 여전히 서로 다른 채널 간의 코드 결합 문제에 직면해 있으며, 이는 이 액세스에서 함께 해결해야 합니다. 그렇지 않으면 상태 머신 액세스 비용이 매우 높아 품질을 보장하기 어려울 것입니다. , 이후 유지 관리가 더 어려워집니다. 위에서 언급한 상태 머신 수정이 이상적인 조건에서 이루어졌다고 해도 다른 수정 없이 여전히 두 가지 문제가 있습니다.

  • 목표 상태 판단 로직의 결합;

  • 실제로 작업을 수행하는 커플링입니다.

상태 머신의 가드(실행 전제 조건이 충족되었는지 여부를 결정하기 위한)와 액션(액션의 실제 실행)의 구현에는 매우 큰 인터페이스가 있으며, 여기에는 타겟을 결정하기 위한 다양한 방법이 포함되어 있다는 것을 간단히 이해할 수 있습니다. 상태를 확인하고 모든 채널에서 서로 다른 작업을 실행하는 경우 특정 채널이 구체적으로 수행하는 작업을 이해하기가 매우 어렵습니다.

문제는 신제품 입고 샘플에 대한 제품 선택 검토 및 비즈니스 리서치 리뷰 인터페이스의 코드에 집중되어 있습니다(이 부분은 신제품 입고 샘플에 대한 비즈니스 로직이 가장 복잡하고 복잡한 부분이기도 합니다). 합격-불합격 논리, 제품 선택 및 비즈니스 조사 논리가 모두 혼합되어 있습니다. 코드가 길어서 읽을 수 없습니다. 또한 대규모 트랜잭션(트랜잭션에서 여러 RPC 호출)에도 문제가 있습니다. 상태 머신이 연결되어 있는 동안 특히 다음을 포함합니다.

  • 다양한 채널의 코드는 전략 모드를 사용하여 분할됩니다.

  • 다양한 상태와 다양한 작업 이벤트 처리 논리는 상태 머신의 다양한 상태 및 이벤트에 대한 보호 및 작업 클래스로 요약됩니다.

  • 서로 다른 채널의 동일한 코드 처리 로직이 개별 코드 모듈로 캡슐화되어 각 채널에서 호출됩니다.

전체적인 변환 방법은 아래 그림과 같습니다.

003.png

8. 기대소득

위에서 알 수 있듯이 상태 머신 액세스이지만 실제로 변환의 두 가지 측면을 완료해야 합니다. 하나는 전체 새로운 프로세스에서 채널과 작업으로 구분된 비즈니스 코드의 분리를 완료하는 것입니다. 변환을 통해 다음을 수행할 수 있었습니다.

  • 등록 제출 및 신제품 검토와 같은 이전 신제품 신청 링크의 주요 거래 문제를 해결합니다.

  • 제품 소스 채널 간의 비즈니스 격리로 인해 코드 변경 범위를 더 쉽게 제어할 수 있고 테스트에 더 도움이 됩니다.

  • 코드 확장성을 향상시키고, 코드 이해의 임계값을 낮추며, 일상적인 요구에 맞는 반복 개발 효율성을 향상시킵니다.

두 번째는 다음을 포함하여 신제품 샘플링 프로세스의 상태 흐름 문제를 해결할 수 있는 상태 머신에 대한 액세스입니다.

  • 학습 및 향후 유지 관리를 용이하게 하기 위해 상태 변경 규칙을 통합하고 중앙 집중적으로 관리합니다.

  • 불법적이고 반복적인 상태 이전을 피하십시오.

  • 새로운 상태와 상태 프로세스 사이의 순서를 조정하는 것이 더 쉬워지고 코드 수정을 더 쉽게 제어할 수 있습니다.

9. 상세설계

채널별로 나누는 이유

서로 다른 제품 소스 채널에서 신제품 샘플을 시작하는 것은 서로 다른 역할이 서로 다른 터미널을 통해 신제품을 제출하는 프로세스입니다. 역할과 터미널의 조합은 고정되어 있으며 역할이나 터미널만으로는 결합할 수 없습니다. 공통된 비즈니스 특성, 특정 역할이 결정되어야 완전한 비즈니스 프로세스가 결정될 수 있습니다.

각 채널별로 신상품을 신청할 수 있는 능력도 다릅니다. 예를 들어 판매자는 상품 정보를 가장 완벽하게 파악하고 있기 때문에 신상품 신청 시 완전한 상품 정보를 작성할 수 있고, 다른 채널보다 비즈니스 프로세스가 더 많습니다. 이에 비해 앱에서는 극히 일부의 상품 정보만 입력할 수 있으며, 한번 거절된 신청서는 수정 및 제출이 불가능합니다. 따라서 서로 다른 채널 간의 차이는 자연스러운 것이며 채널 자체에 따라 계속 존재할 수 있습니다.

따라서 특정 작업에서는 채널별로 분할하는 것이 합리적이고 필요합니다.

비즈니스 운영은 채널별로 분리됩니다.

비즈니스 운영을 위한 공통 인터페이스

신제품 샘플의 여러 중요한 노드에 대한 단일 기록(일괄 작업도 단일 처리로 전환됨) 비즈니스 작업(예: 신제품 신청서 제출, 제품 선택 검토, 비즈니스 연구 검토)을 " 전처리 요청 -> 작업 확인" 으로 추상화할 수 있습니다. " -> 비즈니스 로직 실행 -> 지속성 작업 -> 관련 후처리 작업 "이므로 공통 인터페이스 클래스는 다양한 채널의 신제품 및 샘플에 대한 다양한 비즈니스 작업의 실행 프로세스를 수행하도록 설계되었습니다.

public interface NspOperate<C> {

    /**
     * 支持的商品来源渠道
     * @return
     */
    Integer supportApplyType();

    /**
     * 支持的操作类型
     * @return
     */
    String operateCode();

    /**
     * 请求预处理
     * @param context
     */
    void preProcessRequest(C context);

    /**
     * 校验
     * @param context
     */
    void verify(C context);

    /**
     * 执行业务逻辑
     * @param context
     */
    void process(C context);

    /**
     * 执行持久化
     * @param context
     */
    void persistent(C context);

    /**
     * 后处理
     * @param context
     */
    void post(C context);
}

몇 가지 참고사항:

  • 후속 상태 머신의 각 이벤트는 인터페이스의 작업 유형에 일대일로 대응됩니다.  또한 상태 전송을 포함하지 않는 시나리오에 대해 다른 작업 유형을 정의할 수도 있습니다(예: 신제품 애플리케이션 편집, 신제품 애플리케이션을 기반으로 SPU 생성).

  • 프로세스 방법의 정의는 비교적 광범위합니다. 다양한 비즈니스 운영에서는 실제 실행 내용이 매우 다를 수 있습니다. 예를 들어, 신제품 리뷰를 제출할 때 비즈니스 연구 리뷰에서는 일부 데이터 조립 작업만 수행할 수 있습니다. 이 작업 후에는 목표를 분석해야 합니다. 따라서 하위 클래스는 자체 비즈니스 요구 사항에 따라 구현될 새로운 메서드를 추가로 분할하고 정의할 수 있습니다.

  • 이 방식에만 트랜잭션 추가를 지원하기 위해 Permanent Persistence 방식이 별도로 정의되어 있습니다. 현재 시스템 코드는 실제로 유사한 디자인을 가지고 있지만, 검증, 비즈니스 처리 등 전체 실행 프로세스를 포함하여 트랜잭션이 너무 광범위할 수 있습니다. 이는 대규모 트랜잭션의 중요한 이유 중 하나이기도 합니다. 따라서 이 메서드의 구현은 DB 작업을 읽고 쓰기만 하고 비즈니스 로직은 포함하지 않는다는 것이 분명합니다 .

  • 이 인터페이스의 각 구현은 "제품 소스 채널 + 작업 유형"을 사용하여 Spring Bean 관리를 위한 고유 키를 형성하는 동시에 일부 작업이 제품 소스를 구별하지 않는다는 점을 고려하여 정의할 수 있습니다. 현재 구현을 나타내는 특수 applyType(예: -1)은 모든 채널을 지원합니다. 구현을 가져올 때 현재 채널의 구현을 가져오도록 최적화하세요. 찾지 못한 경우 모든 채널의 구현을 찾아보세요.

public NspOperate getNspOperate(Integer applyType, String operateCode) {
    String key = buildKey(applyType, operateCode);
    NspOperate nspOperate = operateMap.get(key);
    if (Objects.isNull(nspOperate)) {
        String generalKey = buildKey(-1, operateCode);
        nspOperate = operateMap.get(generalKey);
    }
    AssertUtils.throwIf(Objects.isNull(nspOperate), "NspOperate not found! key = " + key);
    return nspOperate;
}

사업운영실시 수업

현재 비즈니스 시나리오에 따르면 일부 코드의 재사용을 촉진하기 위해 비즈니스 운영 구현에는 최대 3가지 수준의 상속 관계가 있습니다.

004.png

  • 첫 번째 수준: 비즈니스 연구 검토와 같은 작업 유형(비즈니스 이벤트)을 집계하기 위한 차원으로, 비즈니스 연구 검토에서 공통 코드 및 사용자 정의 방법을 정의할 수 있습니다. 비즈니스 연구 검토를 위한 일반 입력 매개 변수 확인, 필드가 비어 있지 않음 등등.

  • 두 번째 수준: 비즈니스 연구 검토 - 식별 지원, 비즈니스 연구 검토 - 식별 지원 안 함 등과 같은 작업 유형 차원(비즈니스 이벤트)에 특정합니다. 여기에서 작업 유형에 따라 모든 제품 소스 채널의 공통 코드를 정의할 수 있습니다. 치수. 예를 들어 식별이 지원되지 않는 경우 이유가 필요하며 비즈니스 조사 검토는 여러 시스템에서 일련의 판단 논리를 호출합니다.

  • 세 번째 계층: 제품 소스 채널 수준의 특정 구현으로 상위 클래스의 코드를 재사용할 수 있습니다.

모든 비즈니스 운영에 이러한 세 가지 구현 계층이 필요한 것은 아닙니다. 실제 사용 시 다음과 같은 세 가지 상황이 발생합니다.

  • 레이어는 하나뿐입니다. 제품의 소스 채널에 관계없이 새 제품의 샘플은 무효화됩니다. 모든 채널은 동일한 로직을 사용하며 구현 클래스는 하나만 있으면 충분합니다.

  • 새 제품 신청서를 제출하고 다양한 제품 소스 채널을 구별하는 두 가지 수준만 있습니다.

  • 세 가지 수준이 있습니다: 신제품 상업 연구 검토. 상업 연구 검토는 다음과 같은 여러 작업 유형(비즈니스 이벤트)으로 나뉩니다. 상업 연구 검토 - 식별 지원, 상업 연구 검토 - 식별을 지원하지 않음, 상업 연구 검토 - 시작 샘플 전송 등 각 상품 소스 채널에는 각 작업 유형에 따라 자체 구현이 있습니다.

상태 머신 액세스

상태 머신 정의

위의 상태 흐름도를 보면 신제품과 샘플의 상태 흐름이 상대적으로 명확하지만 실제로는 소스 채널의 불완전한 분할을 방지하기 위해 각 채널의 상태 프로세스에 약간의 차이가 있습니다. 상태 머신 구성 비용도 높지 않으므로 각 채널이 자체 상태 머신 구성을 구축하기로 결정됩니다.

C 측 채널을 예로 들면 상태 머신의 구성은 다음과 같습니다.

@Configuration
@Slf4j
@EnableStateMachineFactory(name = "newSpuApplyStateMachineFactory")
public class NewSpuApplyStateMachineConfig extends EnumStateMachineConfigurerAdapter<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> {

    public final static String DEFAULT_MACHINEID = "spring/machine/commodity/newspuapply";

    @Resource
    private NewSpuApplyStateMachinePersist newSpuApplyStateMachinePersist;

    @Resource
    private NspNewApplyAction nspNewApplyAction;

    @Resource
    private NspNewApplyGuard nspNewApplyGuard;

    @Bean
    public StateMachinePersister<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, NewSpuApplySendEventContext> newSpuApplyMachinePersister() {
        return new DefaultStateMachinePersister<>(newSpuApplyStateMachinePersist);
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> config) throws Exception {
        config.withConfiguration().machineId(DEFAULT_MACHINEID);
    }

    @Override
    public void configure(StateMachineStateConfigurer<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> config) throws Exception {
        config.withStates()
                .initial(NewProductShowEnum.STM_INITIAL)
                .state(NewProductShowEnum.CHECKING)
                .state(NewProductShowEnum.UNPUT_ON_SALE_UNPASS)
                .state(NewProductShowEnum.UNPUT_ON_SALE_PASSED_UNSEND)
                .state(NewProductShowEnum.UNPUT_ON_SALE_PASSED)
                .choice(NewProductShowEnum.STM_UNPUT_ON_SALE_PASSED_UNSEND)
                .choice(NewProductShowEnum.STM_UNPUT_ON_SALE_PASSED)
                .state(NewProductShowEnum.UNPUT_ON_SALE_PASSED_UNSEND_NOT_PUT)
                .state(NewProductShowEnum.OTHER_UNPASS_FOR_SPU_STUDYER)
                .state(NewProductShowEnum.FINSH_SPU)
                .state(NewProductShowEnum.GONDOR_INVALID)
                .states(EnumSet.allOf(NewProductShowEnum.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> transitions) throws Exception {

        transitions.withExternal()
                //提交新的新品申请
                .source(NewProductShowEnum.STM_INITIAL)
                .target(NewProductShowEnum.CHECKING)
                .event(NewSpuApplyStateMachineEventsEnum.NEW_APPLY)
                .guard(nspNewApplyGuard)
                .action(nspNewApplyAction)

                //选品不通过
                .and().withExternal()
                .source(NewProductShowEnum.CHECKING)
                .target(NewProductShowEnum.UNPUT_ON_SALE_UNPASS)
                .event(NewSpuApplyStateMachineEventsEnum.OM_PICK_REJECT)
                .guard(nspOmRejectGuard)
                .action(nspOmRejectAction)

                //选品通过
                .and().withExternal()
                .source(NewProductShowEnum.CHECKING)
                .target(NewProductShowEnum.UNPUT_ON_SALE_PASSED_UNSEND)
                .event(NewSpuApplyStateMachineEventsEnum.OM_PICK_PASS)
                .guard(nspOmPassGuard)
                .action(nspOmPassAction)

                //发起商研审核
                .and().withExternal()
                .source(NewProductShowEnum.UNPUT_ON_SALE_PASSED_UNSEND)
                .target(NewProductShowEnum.STM_UNPUT_ON_SALE_PASSED_UNSEND)
                .event(NewSpuApplyStateMachineEventsEnum.START_BR_AUDIT)

                .and().withChoice()
                .source(NewProductShowEnum.STM_UNPUT_ON_SALE_PASSED_UNSEND)
                .first(NewProductShowEnum.UNPUT_ON_SALE_PASSED, nspStartBrAuditWaitAuditStatusDecide, nspStartBrAuditWaitAuditChoiceAction)
                .then(NewProductShowEnum.UNPUT_ON_SALE_PASSED_UNSEND_NOT_PUT, nspStartBrAuditRejctStatusDecide, nspStartBrAuditRejctChoiceAction)
                .last(NewProductShowEnum.FINSH_SPU, nspStartBrAuditFinishChoiceAction)

                //商研审核-支持鉴别
                .and().withExternal()
                .source(NewProductShowEnum.UNPUT_ON_SALE_PASSED)
                .target(NewProductShowEnum.FINSH_SPU)
                .event(NewSpuApplyStateMachineEventsEnum.BR_HUMAN_AUDIT_SUPPORT_ALL)
                .guard(nspBrAuditSupportAllGuard)
                .action(nspBrAuditSupportAllAction)

                //商研审核-商品信息有误
                .and().withExternal()
                .source(NewProductShowEnum.UNPUT_ON_SALE_PASSED)
                .target(NewProductShowEnum.OTHER_UNPASS_FOR_SPU_STUDYER)
                .event(NewSpuApplyStateMachineEventsEnum.BR_HUMAN_AUDIT_WRONG_INFO)
                .guard(nspBrAuditWrongInfoGuard)
                .action(nspBrAuditWrongInfoAction)

                //商研审核-不支持鉴别
                .and().withExternal()
                .source(NewProductShowEnum.UNPUT_ON_SALE_PASSED)
                .target(NewProductShowEnum.UNPUT_ON_SALE_PASSED_UNSEND_NOT_PUT)
                .event(NewSpuApplyStateMachineEventsEnum.BR_HUMAN_AUDIT_SUPPORT_NONE)
                .guard(nspBrAuditRejectGuard)
                .action(nspBrAuditRejectAction)
        ;
    }
}

상태 머신의 상태는 신제품 샘플 DB 테이블의 상태 필드와 완전히 매핑되며, 상태 머신 이벤트는 위 그림의 이벤트와 완전히 일치합니다.  신제품 샘플에는 이벤트를 수신한 후 대상 상태에 도달하기 위해 일련의 논리적 판단이 필요한 몇 가지 시나리오가 있습니다. 여기서 상태 머신의 선택 상태는 대상 상태의 판단 및 전송을 완료하는 데 사용됩니다. .

상태 머신과 관련된 요소 중 어떤 요소가 독립적으로 분리되고 어떤 요소가 공유되는지 명확하게 살펴보겠습니다.

005.png

채널마다 상태 머신의 구성 클래스만 다르기 때문에 비용이 높지 않다는 것을 알 수 있다. 가드 및 액션 구현 클래스가 모든 채널에서 공유되는 방법은 아래에 설명되어 있습니다.

가드 및 액션 구현

위 상태 머신의 특정 구성에서 볼 수 있듯이 신제품 샘플링 프로세스에는 두 가지 유형의 상태 흐름이 포함됩니다.

  • 이벤트 발생 후 대상 상태는 고정됩니다. 예를 들어, 상품 선택 검토 중에 상품 선택 실패 이벤트가 발생하면 신규 상품 적용 대상 상태는 상품 선택 실패로 결정됩니다.

  • 이벤트가 발생한 후의 목표 상태는 코드 로직에 의해 판단되어야 합니다. 이러한 이유로 상태 머신 구성에 선택 상태가 도입됩니다. 예를 들어 상업적인 연구 검토를 시작하는 경우 신제품의 목표 상태입니다. 애플리케이션은 식별이 직접 지원되지 않거나 신제품 애플리케이션이 직접 통과될 수 있으며, 수동 검토가 필요할 수도 있습니다.

Spring 상태 머신 설계에서 이러한 두 가지 유형의 상태 흐름(gurad 및 action)의 책임은 다릅니다.

006.png

따라서 이 두 가지 유형의 가드와 액션의 구현 논리가 다릅니다.

그러나 동일한 이벤트/선택 상태의 가드와 액션의 경우 제품 소스 채널에 따라 비즈니스 코드 분할이 구현되어 있으므로 서로 다른 제품 소스 채널을 공유할 수 있으며, 특정 NspOperate로만 라우팅하면 됩니다. 구현 비즈니스 구현 클래스이면 충분합니다. 예는 다음과 같습니다.

고정된 대상 상태로 보호:

@Component
public class NspNewApplyGuard extends AbstractGuard<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, NewSpuApplySendEventContext> {

    @Resource
    private NewSpuApplyOperateHelper newSpuApplyOperateHelper;

    @Override
    protected boolean process(StateContext<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> context) {

        final CatetorySendEventContextRequest<NewSpuApplyContext> request = getSendEventContext(context).getRequest();
        NewSpuApplyContext ctx = request.getParams();
        Integer applyType = ctx.getApplyType();     //从业务数据中取出商品来源
        
        NspOperate<NewSpuApplyContext> nspOperate = newSpuApplyOperateHelper.getNspOperate(applyType, NewSpuApplyStateMachineEventsEnum.NEW_APPLY.getCode());   //固定的事件code
        //做请求的预处理
        nspOperate.preProcessRequest(ctx);
        //对业务数据做校验,校验不通过即抛出异常
        nspOperate.verify(ctx);

        //正常执行完上述2个方法,代表是可以执行的
        return Boolean.TRUE;
    }
}

보호하려면 제품 소스 및 수정된 이벤트 코드를 기반으로 NspOperate 구현 클래스를 얻고 preProcessRequest를 호출하고 NspOperate의 verify 메서드만 호출하면 검증이 완료됩니다.

고정된 대상 상태를 사용한 작업:

@Component
public class NspNewApplyAction extends AbstractSuccessAction<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, CategorySendEventContext> {

    @Resource
    private NewSpuApplyOperateHelper newSpuApplyOperateHelper;

    @Override
    protected void process(StateContext<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> context) {
        final CatetorySendEventContextRequest<NewSpuApplyContext> request = getSendEventContext(context).getRequest();
        NewSpuApplyContext ctx = request.getParams();
        Integer applyType = ctx.getApplyType();    //从业务数据中取出商品来源
        
        NspOperate<NewSpuApplyContext> nspOperate = newSpuApplyOperateHelper.getNspOperate(applyType, NewSpuApplyStateMachineEventsEnum.NEW_APPLY.getCode());   //固定的事件code

        //执行业务逻辑
        nspOperate.process(ctx);
        //持久化
        nspOperate.persistent(ctx);
        //后处理
        nspOperate.post(ctx);
    }
}

이 작업에서는 제품 소스 및 고정 이벤트 코드를 기반으로 NspOperate 구현 클래스도 획득하고 NspOperate의 마지막 몇 가지 메서드를 호출하여 비즈니스 작업을 완료합니다.

Guard in Choice 상태:

Guard는 현재 채널과 이벤트를 기반으로 대상 상태를 결정해야 합니다. 여기서는 Guard가 호출을 구현하기 위해 별도의 인터페이스가 추상화됩니다. NspOperate에서 유사한 로직이 필요한 경우 이 별도의 인터페이스도 참조할 수 있으므로 코드는 없습니다. 복사:

public interface NspStatusDecider<C, R> {

    /**
     * 支持的商品来源渠道
     * @return
     */
    Integer supportApplyType();

    /**
     * 支持的操作类型
     * @return
     */
    String operateCode();

    /**
     * 判定目标状态
     * @param context
     */
    R decideStatus(C context);
}
@Component
public class NspBrAuditNoIdentifyGuard extends AbstractGuard<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, NewSpuApplySendEventContext> {

    @Resource
    private NewSpuApplyOperateHelper newSpuApplyOperateHelper;

    @Override
    protected boolean process(StateContext<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> context) {

        final CatetorySendEventContextRequest<NewSpuApplyContext> request = getSendEventContext(context).getRequest();
        NewSpuApplyContext ctx = request.getParams();
        Integer applyType = ctx.getApplyType();     //从业务数据中取出商品来源

        NspStatusDecider<NewSpuApplyContext, Result> nspStatusDecider = newSpuApplyOperateHelper.getNspStatusDecider(applyType, NewSpuApplyStateMachineEventsEnum.BR_HUMAN_AUDIT_SUPPORT_NONE.getCode());   //固定的事件code
        //判定目标状态
        Result result = nspStatusDecider.decideStatus(ctx);
        ctx.setResult(result);  //将判定结果放入上下文,其他的guard可以引用结果,避免重复判断
        
        return Result.isSuccess(result);    //根据判定结果决定是否匹配当前guard对应的目标状态
    }
}

선택 상태의 작업:

@Component
public class NspBrAuditNoIdentifyAction extends AbstractSuccessAction<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, CategorySendEventContext> {

    @Resource
    private NewSpuApplyOperateHelper newSpuApplyOperateHelper;

    @Override
    protected void process(StateContext<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> context) {
        final CatetorySendEventContextRequest<NewSpuApplyContext> request = getSendEventContext(context).getRequest();
        NewSpuApplyContext ctx = request.getParams();
        Integer applyType = ctx.getApplyType();    //从业务数据中取出商品来源

        NspOperate<NewSpuApplyContext> nspOperate = newSpuApplyOperateHelper.getNspOperate(applyType, NewSpuApplyStateMachineEventsEnum.BR_HUMAN_AUDIT_SUPPORT_NONE.getCode());   //固定的事件code

        //做请求的预处理
        nspOperate.preProcessRequest(ctx);
        //对业务数据做校验
        nspOperate.verify(ctx);
        //执行业务逻辑
        nspOperate.process(ctx);
        //持久化
        nspOperate.persistent(ctx);
        //后处理
        nspOperate.post(ctx);
    }
}

고정된 대상 상태를 갖는 작업과의 유일한 차이점은 NspOperate의 preProcessRequest 및 verify 메서드가 실행된다는 것입니다.

서로 다른 채널 간에 서로 다른 보호 및 작업 구현을 사용하는 대신 별도의 전략 클래스를 사용하여 다음 두 가지 고려 사항에 대해 서로 다른 채널 구현을 나눕니다.

  • 상태 기계 구현을 대체하는 것이 가능하므로 상태 기계 구현과 관련된 코드는 비즈니스 논리 코드와 결합될 것으로 예상되지 않습니다.

  • 상태 머신이 포함되지 않은 시나리오에서는 신제품 애플리케이션 편집 등과 같이 로직을 채널별로 분할해야 할 필요도 있습니다.

제품 출시 과정 중 SPU 상태 흐름과 연계

새 제품 샘플이 "Product Information Pending Review" 상태가 되면 SPU 상태 머신 프로세스가 후속 SPU 상태 흐름을 이어받습니다. SPU 상태가 "Audit Passed"에 도달하면 새 제품 샘플 상태가 상업 연구로 이어집니다. 그리고 검토 단계. 이 기간 동안 SPU의 각 정보 및 상태 변경은 신제품 입고 샘플에 대해(MQ 또는 인애플리케이션 이벤트를 통해) 통보되어야 하며, 이후 신제품 입고 샘플 기록에 대해 해당 비즈니스 처리가 수행됩니다.

후속 확장 분석

신제품 적용 프로세스에 포함될 수 있는 향후 변경 사항에 대해 이 변환의 확장성을 평가하십시오.

새로운 제품 소스 채널 추가

기존 채널에 영향을 주지 않고 새 채널에 대한 다양한 비즈니스 작업 및 이벤트를 구현하도록 새 상태 머신을 구성하기만 하면 됩니다.

새 제품 샘플에 대한 상태 노드가 추가되었습니다.

상태 머신 구성을 수정하고 새 이벤트와 해당 구현 클래스를 추가하기만 하면 됩니다.

신제품 입고 샘플의 상태 간 순서 조정

상태 머신 구성을 수정하고 관련된 비즈니스 운영 구현 클래스의 수정을 평가합니다. 수정 범위가 명확하고 제어 가능합니다.

10. 요약

우리는 전략 모델을 사용하여 다양한 제품 소스 채널의 비즈니스 로직을 분리하고, 공통성을 유지하며, 자체적인 차별화된 로직을 구현하여 상태 머신 도입을 통해 향후 비즈니스 요구 사항 변화에 대한 확장성을 제공하고, 상태를 명확하게 하고 표준화합니다. Flow는 상태가 정확하고 합법적으로 이전되도록 보장하는 동시에 향후 비즈니스 프로세스 변경을 위한 견고한 기반을 마련합니다.

한편으로 이러한 변환은 현재 구현의 완고한 문제를 해결하고 기존 코드를 시작하는 데 따른 어려움을 줄이는 한편, 향후 새로운 소스를 추가할지 여부도 고려합니다. 채널을 변경하거나 비즈니스 프로세스를 수정하더라도 코드 수정 범위는 추가 작업 부하 없이 제어 및 측정 가능하며 비즈니스를 보다 효과적이고 안전하며 안정적으로 지원할 수 있습니다.

* 텍스트/스위트 오렌지

 

이 기사는 Dewu Technology의 원본입니다. 더 흥미로운 기사를 보려면 Dewu Technology 공식 웹사이트를 참조하세요.

Dewu Technology의 허가 없이 전재하는 것은 엄격히 금지되어 있으며, 그렇지 않을 경우 법에 따라 법적 책임을 추궁할 것입니다!

고등학생들이 성인식으로 자신만의 오픈소스 프로그래밍 언어를 만든다 - 네티즌들의 날카로운 논평: 애플은 방어에 의존해 만연한 사기로 인해 국내 서비스가 중단됐다 . 앞으로는 윈도 플랫폼 타오바오(taobao.com)에서 독립 게임을 제작할 계획이다. 웹 버전 최적화 작업을 다시 시작해 프로그래머들의 종착지, 비주얼 스튜디오 코드 1.89에서 가장 많이 쓰이는 자바 LTS 버전인 자바 17이 출시되고, 윈도 10에는 시장 점유율 70%, Windows 11은 계속해서 하락
{{o.이름}}
{{이름}}

추천

출처my.oschina.net/u/5783135/blog/11105548