首页 > IT基础架构 > 服务器/存储 > 商品上新业务状态机接入实践 | 得物技术
来源:得物技术
目录
一、商品上新业务介绍
二、为什么考虑接入状态机
三、商品上新流程中涉及的状态
1. 新品来样状态枚举
2. SPU状态枚举
四、新品来样所有事件
五、新品来样状态流转
六、状态机技术选型
七、状态机接入面临的困难
八、预期收益
九、详细设计
1. 按渠道拆分的合理性
2. 业务操作按渠道解耦
2.1 业务操作通用接口
2.2 业务操作实现类
3. 状态机接入
3.1 状态机定义
3.2 Guard与Action的实现
3.2.1 目标状态固定的guard
3.2.2 目标状态固定的action
3.2.3 Choice state中的guard
3.2.4 Choice state中的action
3.3 商品上新过程中与SPU状态流转的联动
4. 后续扩展分析
4.1 新增商品来源渠道
4.2 新品来样新增状态节点
4.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状态枚举
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审核驳回超过X天
发起寄样
寄样进度更新
五
新品来样状态流转

六
状态机技术选型
七
状态机接入面临的困难
目前新品来样的代码中还面临着不同渠道之间代码耦合的问题,需要在本次接入中一起解决,否则状态机接入的成本会很高,质量也难以保证,后续维护更加困难。即使理想状态下经过了上述的状态机的改造,不进行其他改造,还会存在两方面的问题:
问题集中反映在新品来样的选品审核、商研审核接口的代码中(这部分也是新品来样业务逻辑最多最复杂的部分),它夹杂了所有渠道所有通过不通过的逻辑、选品和商研的逻辑,全部糅合在一起,代码冗长且可读性不好,同时还存在大事务的问题(事务中多次RPC调用),因此在状态机接入的同时需要将这些代码进行拆分和合并,具体包括:

八
预期收益
从上文可以了解到,虽然是状态机接入,实际上是要完成两方面的改造,一是完成对整个上新流程中分渠道、分操作的业务代码的解耦,这部分的改造,能够:
提高代码的可扩展性,降低代码理解门槛,提高日常需求的迭代开发效率。
二是状态机的接入,可以解决新品来样流程中的状态流转问题,包括:
九
详细设计
按渠道拆分的合理性
业务操作按渠道解耦
业务操作通用接口
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);}
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;}业务操作实现类

状态机接入
状态机定义
4j(name = "newSpuApplyStateMachineFactory")public class NewSpuApplyStateMachineConfig extends EnumStateMachineConfigurerAdapter<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> {public final static String DEFAULT_MACHINEID = "spring/machine/commodity/newspuapply";private NewSpuApplyStateMachinePersist newSpuApplyStateMachinePersist;private NspNewApplyAction nspNewApplyAction;private NspNewApplyGuard nspNewApplyGuard;public StateMachinePersister<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, NewSpuApplySendEventContext> newSpuApplyMachinePersister() {return new DefaultStateMachinePersister<>(newSpuApplyStateMachinePersist);}public void configure(StateMachineConfigurationConfigurer<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum> config) throws Exception {config.withConfiguration().machineId(DEFAULT_MACHINEID);}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));}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);}}

Guard与Action的实现

public class NspNewApplyGuard extends AbstractGuard<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, NewSpuApplySendEventContext> {private NewSpuApplyOperateHelper newSpuApplyOperateHelper;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;}}
public class NspNewApplyAction extends AbstractSuccessAction<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, CategorySendEventContext> {private NewSpuApplyOperateHelper newSpuApplyOperateHelper;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);}}
public interface NspStatusDecider<C, R> {/*** 支持的商品来源渠道* @return*/Integer supportApplyType();/*** 支持的操作类型* @return*/String operateCode();/*** 判定目标状态* @param context*/R decideStatus(C context);}
public class NspBrAuditNoIdentifyGuard extends AbstractGuard<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, NewSpuApplySendEventContext> {private NewSpuApplyOperateHelper newSpuApplyOperateHelper;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对应的目标状态}}
public class NspBrAuditNoIdentifyAction extends AbstractSuccessAction<NewProductShowEnum, NewSpuApplyStateMachineEventsEnum, CategorySendEventContext> {private NewSpuApplyOperateHelper newSpuApplyOperateHelper;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);}}
商品上新过程中与SPU状态流转的联动
后续扩展分析
新增商品来源渠道
新品来样新增状态节点
新品来样调整状态间顺序
十
小结
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70024420/viewspace-3013701/ ,如需转载,请注明出处,否则将追究法律责任。