製品発売のための新しいビジネス ステート マシンへのアクセスの実践

1. 新製品立ち上げ事業のご紹介

001.png

製品リストとは、Dewu プラットフォームに新製品を登録することを意味します。完全な製品リスト プロセスは、さまざまなソースおよびチャネルからの新製品申請を提出することから始まり、主に次のようなさまざまな役割による複数回のレビューを経る必要があります。

  • 製品選択のレビュー: 新製品申請で提出された情報に基づいて、棚の要件を満たしているかどうかを判断します。

  • 製品データのレビュー: 複数回のビジネス管理、リスク管理、法的レビューを含む、製品データの正確性と完全性をレビューします。

  • ビジネス リサーチ レビュー: ビジネス リサーチ レビューは、プラットフォーム上での製品の識別およびサポート能力を判断するものであり、これは Dewu ビジネスの特徴でもあります。

これらのレビューでは、製品選択レビューとビジネス調査レビューは、製品の新しいビジネスにのみ適用され、製品データのレビューが行われます。製品データ処理プロセスに属しており、現在の製品情報が C 側の表示要件を満たしているかどうかを判断します。

したがって、システムの実装には、新製品入荷サンプル プロセスと製品データ処理プロセスのステート フローが関与する必要があります。前者は新製品入荷サンプル テーブルに関係し、後者は主に製品 SPU メイン テーブルに関係します。 Access では、新製品入荷サンプル プロセスのフローとステート マシンに焦点を当てています。新製品サンプリング プロセスのソース チャネル属性は非常に明白であり、さまざまなチャネルのビジネス ロジックとプロセスには大小の違いがあります。

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. 新製品サンプルに関する各種イベント

  • 新しい製品のドラフトを保存する

  • 新製品の申請を提出する

  • 製品の選択に合格しました

  • 製品の選択に失敗しました

  • 製品の選択が拒否された後に再送信する

  • ビジネス調査のレビューを開始する

  • ビジネスリサーチレビュー - サポート識別

  • ビジネスリサーチのレビュー - 識別はサポートされていません

  • ビジネスリサーチレビュー - 製品情報が間違っています

  • SPU 審査が X 日以上拒否されました

  • サンプルの送信を開始する

  • サンプル納品の進捗状況の更新

合計12個。

5. 新製品サンプルステータス転送

前述したように、製品ソー​​ス チャネルごとに新製品発売プロセスも異なります。つまり、チャネルごとにステータス フローも異なります。以下は B 側販売者チャネルの図です。

002.png

図中、オレンジ色の枠が新製品サンプルの入荷状況、緑の枠がSPUの状態、青の丸枠が状態変化のきっかけとなるイベント、矢印で繋がった箇所が現状からの流れを表しています。次の状態へ。

特定のイベントがトリガーされた場合、転送する必要があるターゲット状態は固定されていないことに注意してください。イベントが最終的に流れるターゲット状態を決定するには、一連の論理的判断が必要です。

6. ステートマシンテクノロジーの選択

実際のステート マシン フレームワークとして Spring StateMachine を選択します。具体的なプロセスと詳細については、この記事を参照してください。 https://mp.weixin.qq.com/s/TqXMtS44D4w6d1-KLxcoiQこの記事では詳しく説明しません。

7. ステートマシンへのアクセスが直面する困難

現時点では、新製品サンプルのコードは依然として異なるチャネル間のコード結合の問題に直面しており、このアクセスで一緒に解決する必要があります。そうしないと、ステート マシンへのアクセスのコストが非常に高くなり、品質を保証することが困難になります。 、その後のメンテナンスがより困難になります。たとえ上記のステート マシンの変更が理想的な条件下で行われ、他の変更がなかったとしても、依然として 2 つの問題が残ります。

  • 目標状態判定ロジックの結合;

  • 実際にアクションを実行するカップリング。

ステート マシンのガード (実行の前提条件が満たされているかどうかを判断するため) とアクション (アクションの実際の実行) の実装には非常に大きなインターフェイスがあり、ターゲットを決定するためのさまざまなメソッドが含まれていることが簡単に理解できます。コードを読んで、特定のチャネルが具体的に何をするのかを理解するのは非常に困難です。

問題は、新製品入荷サンプルの製品選択レビューとビジネス調査レビュー インターフェイスのコードに集中しています (この部分は、新製品入荷サンプルの最も複雑なビジネス ロジックを備えた部分でもあります)。合格/失敗ロジック、製品選択ロジック、およびビジネス調査ロジックがすべて混在しているため、コードが長くなり、読みにくくなります。したがって、これらのコードは分割する必要があります。ステート マシンが接続されている間、特に以下が含まれます。

  • さまざまなチャネルのコードは、戦略モードを使用して分割されます。

  • さまざまな状態とさまざまな操作イベント処理ロジックは、ステート マシンのさまざまな状態とイベントのガード クラスとアクション クラスに要約されます。

  • 異なるチャネルの同じコード処理ロジックは、個別のコード モジュールにカプセル化され、各チャネルで呼び出されます。

全体的な変換方法を次の図に示します。

003.png

8. 期待される収入

上記からわかるように、これはステート マシン アクセスではありますが、実際には変換の 2 つの側面を完了する必要があります。1 つは、新しいプロセス全体でチャネルとオペレーションに分割されたビジネス コードの分離を完了することです。変換により、次のことが可能になりました。

  • 以前の新製品申請リンクにおける主要なトランザクションの問題 (登録の送信や新製品のレビューなど) を解決します。

  • 製品ソース チャネル間のビジネスの分離により、コード変更の範囲がより制御しやすくなり、テストがより容易になります。

  • コードのスケーラビリティを向上させ、コード理解のしきい値を下げ、日常のニーズに合わせた反復開発の効率を向上させます。

2 つ目はステート マシンへのアクセスです。これにより、次のような新製品サンプリング プロセスにおけるステート フローの問題を解決できます。

  • ステータス変更ルールを一元管理し、学習とその後のメンテナンスを容易にします。

  • 違法で反復的なステータス転送を避けてください。

  • 新しい状態と状態プロセスの間の順序を調整することが容易になり、コードの変更がより制御しやすくなります。

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

いくつかのメモ:

  • 後続のステートマシンの各イベントは、インターフェイスの動作タイプと 1 対 1 に対応します。 さらに、状態転送を含まないシナリオ (例: 新しい製品アプリケーションの編集、新しい製品アプリケーションに基づく SPU の作成) に対して、他の操作タイプを定義することもできます。

  • プロセス メソッドの定義は比較的幅広く、ビジネス オペレーションによっては、実際の実行内容が大きく異なる場合があります。たとえば、新製品のレビューを送信する場合は、一部のデータ アセンブリ アクションのみを実行することもあります。この操作の後に目標を分析する必要があります。したがって、サブクラスは、独自のビジネス ニーズに基づいて実装される新しいメソッドをさらに分割して定義できます。

  • 永続的な永続化メソッドは、このメソッドへのトランザクションの追加のみをサポートするために別途定義されています。実際には、現在のシステム コードも同様の設計になっていますが、トランザクションは検証や業務処理などの実行プロセス全体を含む広範なものである可能性があります。これは、大規模なトランザクションの重要な理由の 1 つでもあります。したがって、このメソッドの実装には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

  • 第 1 レベル: ビジネス リサーチ レビューなどの操作タイプ (ビジネス イベント) を集計するためのディメンション。ビジネス リサーチ レビューで共通のコードとカスタム メソッドを定義できます。たとえば、ビジネス リサーチ レビューの一般的な入力パラメーターの検証、フィールドは空ではありません。など。

  • 第 2 レベル: ビジネス リサーチ レビュー - 識別をサポートする、ビジネス リサーチ レビュー - 識別をサポートしないなど、操作タイプのディメンション (ビジネス イベント) に固有です。ここでは、操作タイプの下ですべての製品ソース チャネルの共通コードを定義できます。寸法。例: 識別がサポートされていない場合、およびビジネス調査レビューで複数のシステムから一連の判断ロジックを呼び出す場合、理由が必要です。

  • 3 番目の層: 製品ソース チャネル レベルでの特定の実装。親クラスのコードを再利用できます。

すべての業務運営でこれら 3 つの層の実装が必要なわけではありません。実際の使用では、次の 3 つの状況が発生します。

  • 層は 1 つだけです。製品のソース チャネルに関係なく、新しい製品のサンプルは無効になり、すべてのチャネルで同じロジックが使用され、実装クラスは 1 つだけで十分です。

  • レベルは 2 つだけです。新しい製品の申請を提出することと、さまざまな製品ソース チャネルを区別することです。

  • 3 つのレベルがあります。 新製品の商業調査レビューは、商業調査のレビュー - 識別をサポートする、商業調査のレビュー - 識別をサポートしない、商業調査のレビュー - 開始など、複数の操作タイプ (ビジネス イベント) にも分かれています。サンプルの送信など、各コモディティ ソース チャネルには、各操作タイプの下で独自の実装があります。

ステートマシンへのアクセス

ステートマシンの定義

上記のステータス フロー図から判断すると、新製品とサンプルのステータス フローは比較的明確ですが、実際には、ソース チャネルの不完全な分割を避けるために、各チャネルのステータス プロセスに若干の違いがいくつかあります。ステート マシン構成のコストも高くないため、各チャネルが独自のステート マシン構成を構築することが決定されます。

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

ステートマシンの構成クラスがチャネルごとに異なるだけなので、コストは高くないことがわかります。ガードおよびアクション実装クラスがすべてのチャネルでどのように共有されるかについては、以下で説明します。

ガードとアクションの実装

上記のステート マシンの特定の構成からわかるように、新製品サンプリング プロセスには 2 種類のステート フローが含まれます。

  • イベントのトリガー後のターゲット ステータスは固定されています。たとえば、製品選択レビュー中に製品選択失敗イベントがトリガーされた場合、新製品アプリケーションのターゲット ステータスは製品選択失敗として決定されます。

  • イベントをトリガーした後の目標状態はコード ロジックによって判断する必要があるため、たとえば、新製品の目標状態を商業研究で開始する場合には、選択状態がステート マシン構成に導入されます。アプリケーションは識別が直接サポートされていない場合や、新製品アプリケーションが直接通過する場合があり、手動レビューが必要になる場合もあります。

Spring ステート マシンの設計では、これら 2 種類のステート フロー、grad と action の責任は異なります。

006.png

したがって、これら 2 種類のガードとアクションの実装ロジックは異なります。

ただし、同じイベント/選択状態にあるガードとアクションについては、異なる製品ソース チャネルを共有できます。これは、製品ソー​​ス チャネルに従って分割されたビジネス コードが実装されており、コンポーネント内の特定の 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;
    }
}

Guard では、製品ソー​​スと固定イベント コードに基づいて NspOperate 実装クラスを取得し、NspOperate の preProcessRequest メソッドと 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 の最後のいくつかのメソッドが呼び出されてビジネス オペレーションが完了します。

Choice 状態のガード:

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对应的目标状态
    }
}

Choice 状態のアクション:

@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 メソッドが実行されることです。

異なるチャネル間で異なるガード実装とアクション実装を使用する代わりに、次の 2 つの考慮事項に基づいて、別個の戦略クラスを使用して異なるチャネル実装を分割します。

  • ステート マシンの実装は置き換えることができるため、ステート マシンの実装に関連するコードがビジネス ロジック コードと結合されることは想定されていません。

  • ステート マシンが関与しないシナリオでは、新製品アプリケーションの編集など、チャネルごとにロジックを分割する必要もあります。

製品発売時のSPUステータスフローとの連携

新製品サンプルが「製品情報保留中」ステータスになると、SPU ステート マシン プロセスが後続の SPU ステータス フローを引き継ぎます。SPU ステータスが「監査合格」に達すると、新製品サンプル ステータスは商業調査に送られます。そして検討段階。この期間中、SPU の各情報およびステータス変更は、新製品入荷サンプルを (MQ またはアプリケーション内イベントを通じて) 通知する必要があり、その後、対応するビジネス処理が新製品入荷サンプル レコードに対して実行されます。

その後の拡張分析

新製品の適用プロセスに含まれる可能性のある将来の変更については、この変換のスケーラビリティを評価してください。

新しい製品ソース チャネルを追加する

新しいステート マシンを構成するだけで、既存のチャネルに影響を与えることなく、新しいチャネルのさまざまなビジネス操作やイベントを実装できます。

新製品サンプルのステータス ノードを追加しました

ステート マシンの構成を変更し、新しいイベントと対応する実装クラスを追加するだけです。

新製品入荷サンプルの状態間の順序を調整する

ステート マシンの構成を変更し、関連するビジネス オペレーション実装クラスの変更を評価します。変更の範囲は明確で制御可能です。

10. まとめ

当社は、戦略モデルを使用して、さまざまな製品ソース チャネルのビジネス ロジックを分離し、共通性を維持し、独自の差別化されたロジックを実装して、ステート マシンの導入を通じて将来のビジネス要件の変化に対応できる拡張性を提供します。新しい製品プロセスは、将来のビジネス プロセスの変更に向けた強固な基盤を築きながら、ステータスが正しく合法的に転送されることを保証します。

この変換により、現在の実装における頑固な問題が解決され、既存のコードを使い始める際の困難が軽減される一方で、将来的に新しいソースを追加するかどうかも考慮されます。チャネルやビジネスプロセスの変更に伴うコード変更の範囲を保証し、追加の作業負荷を加えることなく制御可能であり、ビジネスをより効果的かつ安全かつ安定的にサポートできます。

文/スイートオレンジ

 

この記事は Dewu Technology によるものです。さらに興味深い記事については、 Dewu Technology 公式 Web サイトをご覧ください。

Dewu Technology の許可なく転載することは固く禁じられています。さもなければ、法律に従って法的責任が追及されます。

高校生が成人式として独自のオープンソースプログラミング言語を作成―ネットユーザーの鋭いコメント: アップル、M4チップ RustDeskをリリース 不正行為横行で国内サービス停止 雲峰氏がアリババを辞任。将来的には、Windows プラットフォームの タオバオ (taabao.com) で独立したゲームを制作する予定です。Web バージョンの最適化作業を再開し、 プログラマの目的地、 Visual Studio Code 1.89 が最も一般的に使用される Java LTS バージョンである Java 17 をリリースします。Windows 10 には、市場シェアは70%、Windows 11は減少し続けるOpen Source Daily | GoogleはオープンソースのRabbit R1を支持、Microsoftの不安と野心;
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/5783135/blog/11105548