陈德付(星翼)
一、介绍前言
在面对平时的业务功需求迭代时,相信大家都会面临一个代码复杂度和后续的维护高成本的问题,下面我们主要针对营销返利活动场景,对复杂的业务场景的代码设计进行详细的讲解。
二、微中台研发复杂度背景
1、负责的业务中,规则比较多,大部分情况大家是不是会不自觉的试图使用if...else...解决一切问题,这样就会存在一个问题,逻辑在日常的需求迭代中是不是会变的越来越复杂?越来越乱?可读性越来越差?最终慢慢的就会演变成一个看不懂,改不动的黑盒子,没有人搞清楚黑盒子里面到底发生了什么?测试的可测性也大大的降低。
2、在实际的业务开发中,业务场景多,迭代频繁,变化快,规则可能由很多人掌握,没有办法通过一个人了解整个业务规则的全貌的。还有加上业务在行业固有的复杂度和历史包袱,这些问题都会让我们感到痛苦。不知道在哪里改代码,如何下手,改了这块代码会不会带来其他的影响,影响面无法评估。
3、每次需求迭代,研发人力成本投入过多,测试回归成本较高,对应业务需求支撑比较慢,目前比较繁重,对新业务的支持,需要大量的人力成本投入。
三、针对营销活动产品框架
- 在了解设计背景下,先看下目前主推的营销返利活动的几个玩法。
活动类型 | 描述 |
---|---|
单品返利 | 购买指定活动商品,进行活动返利。 |
普通累返 | 累计活动期限内,按照下单量或者下单GMV,进行奖励累计发放。 |
实货累返 | 活动推广门店购买提货卡,后面再转成实货的时候,再返点,让门店自提优惠券,或系统自动发放对应奖励。 |
年框累返 | 以年为单位,进行签署奖励合同,按照下单GMV为坎级算返点,按转实货部分进行算奖励,自提发放优惠券。 |
抽奖 | 达到一定条件,给用户抽奖机会。 |
四、架构框架演变升级过程
原代码设计,在面对业务需求功能开发时
- 直接烟囱式开发,没有组件沉淀
- 逻辑全部耦合在各自的service中,业务代码复用度不高,
- 针对不同场景的活动玩法,没有统一消息入口。
- 每次业务需求迭代,改动的地方比较大,成本高,不知道从何处下手
- 影响面评估难度大,导致测试回归的工作量也变大。
于是针对这类的问题做了概念上的抽象和代码框架的设计:
返利型业务的抽象,实际本质就是了解每种业务玩法特性公共点的抽象,app层从上到下过程分析,模型层从下而上分析结合。能力下层保持模型不断演进,能力下层标准:复用,内聚。
我们举个例子:
于是就推演升级出现有架构模式,收敛消息入口,抽象大的的业务领域模块,沉淀通用组件,不同业务域的解耦,提高内部业务逻辑的复用性和高内聚。抽象一套业务执行框架,让开发更专注每个业务需求本身的业务逻辑代码开发。如下图:
整体业务模块的系统架构:
沉淀通用组件,在复用通用场景组件时,只需编排已有组件,更快的支持业务需求的迭代。
五、详细代码设计实现
代码业务框架和业务逻辑分离,实际业务开发中更关注业务模块的开发。详细对应代码设计实现模型:
框架骨架代码执行类图和业务模块核心类图和模型关系:
骨架框架业务执行时序图:
框架骨架核心代码实现伪代码:
- /**
- * <功能介绍><br>
- * <p>
- * <>
- *
- * @author xy on 2022/2/25.
- * @see [相关类/方法](可选)
- * @since [产品/模块版本] (可选)
- */
- @Service
- public class RebateBizOptServiceImpl implements RebateBizOptService {
- private static final YtLogger LOGGER = YtLoggerFactory.getLogger(RebateBizOptServiceImpl.class);
- private static final Long SMALL_AMOUNT = 1L;
- @Resource
- private RebateRefundShareAmountComponent rebateRefundShareAmountComponent;
- @Resource
- private CacheProxy cacheProxy;
- @Resource
- private RebateBizExecutor rebateBizExecutor;
- /**
- * 门店自提现金券发奖处理
- *
- * @param rebateActivityGiftExtract 提取优惠券参数
- *
- * @return 处理结果
- */
- @Override
- public Boolean rebateActivityGitExtract(RebateActivityGiftExtractDTO rebateActivityGiftExtract) {
- RebateActivityGiftExtractValidator.validate(rebateActivityGiftExtract);
- rebateBizExecutor.execute(MultiRebateCmdDTO.builder()
- .rebateSceneCode(RebateSceneEnum.PRIZE.getSceneCode())
- .rebateActivityGiftExtract(rebateActivityGiftExtract)
- .async(RebateSceneEnum.PRIZE.getAsync())
- .build());
- return Boolean.TRUE;
- }
- /**
- * 交易订单支付完成,计算流水处理
- *
- * @param trade 支付交易订单信息
- * @param rebateBizHandle 业务模块
- */
- @Override
- public void rebateActForPaid(TradeDTO trade, Integer rebateBizHandle) {
- rebateBizExecutor.execute(MultiRebateCmdDTO.builder()
- .rebateSceneCode(RebateSceneEnum.PAID.getSceneCode())
- .trade(trade)
- .async(RebateSceneEnum.PAID.getAsync())
- .rebateBizHandle(rebateBizHandle)
- .build()
- );
- }
- /**
- * 退款关闭,返利逻辑处理
- *
- * @param orderRefund 退款订单信息
- * @param rebateBizHandle 业务模块
- *
- * @return 处理结果
- */
- @Override
- public void rebateActForRefundClose(OrderRefundDTO orderRefund, Integer rebateBizHandle) {
- rebateBizExecutor.execute(MultiRebateCmdDTO.builder()
- .rebateSceneCode(RebateSceneEnum.REFUND_CLOSED.getSceneCode())
- .orderRefund(orderRefund)
- .async(RebateSceneEnum.REFUND_CLOSED.getAsync())
- .rebateBizHandle(rebateBizHandle)
- .build()
- );
- }
- }
- /**
- * <功能介绍><br>
- * <p>
- * <返利业务执行器>
- *
- * @author xy on 2022/6/14.
- * @see [相关类/方法](可选)
- * @since [产品/模块版本] (可选)
- */
- @Service
- public class RebateBizExecutor {
- private static final YtLogger LOGGER = YtLoggerFactory.getLogger(RebateBizExecutor.class);
- @Resource
- private SkeletonHandleBuildBiz skeletonHandleBuildBiz;
- @Resource
- private DefaultRebateHandleFrameBuilder defaultRebateHandleFrameBuilder;
- @Resource
- private YearRebateHandleFrameBuilder yearRebateHandleFrameBuilder;
- @Resource
- private MaterialRebateHandleFrameBuilder materialRebateHandleFrameBuilder;
- /**
- * 返利中台骨架业务执行
- *
- * @param multiRebateCmd 执行参数
- *
- * @return 处理结果
- */
- public List<MultiRebateResult> execute(MultiRebateCmdDTO multiRebateCmd) {
- //请求参数检查验证
- validateRebateCmdParams(multiRebateCmd);
- //初始化上下文信息
- MultiRebateContext context = MultiRebateContextDelegation.initContext(multiRebateCmd);
- //获取通用资源数据
- fetchCommonResource(context);
- //加载返利骨架窗口
- loadHandleFrame(context);
- //执行骨架构建和业务模块处理
- List<MultiRebateResult> resultList = skeletonHandleBuildBiz.buildHandles(context);
- //返回结果解析聚合
- return assembleResults(resultList, multiRebateCmd);
- }
- /**
- * 返回结果解析聚合
- *
- * @param resultList 业务执行结果
- * @param multiRebateCmd 请求入参
- *
- * @return 解析后的处理结果
- */
- private List<MultiRebateResult> assembleResults(List<MultiRebateResult> resultList, MultiRebateCmdDTO multiRebateCmd) {
- List<MultiRebateResult> failResults = StreamUtil.filter(resultList, BaseRebateResult::filterFail);
- if (CollectionUtils.isEmpty(failResults)) {
- return resultList;
- }
- LOGGER.src(REBATE_BIZ_FRAMEWORK).warn("开始执行返利业务处理,参数信息:{},处理失败结果:{}", JSONObject.toJSONString(multiRebateCmd), JSONObject.toJSONString(failResults));
- throw new SmcException(Boolean.TRUE, failResults.get(0).getMessage());
- }
- /**
- * 加载返利骨架窗口
- *
- * @param context 上下文信息
- */
- private void loadHandleFrame(MultiRebateContext context) {
- HandleFrame handleFrame;
- if (RebateBizHandleEnum.YEAR_HANDLE.eq(context.getRebateBizHandle())
- || (RebateSceneEnum.PRIZE.equal(context.getRebateSceneCode())) && PromotionActTypeEnum.REBATE_ACTIVITY.equal(context.getRebateActivityGiftExtract().getActType())) {
- handleFrame = yearRebateHandleFrameBuilder.build(context);
- } else if (RebateBizHandleEnum.MATERIAL_HANDLE.eq(context.getRebateBizHandle())
- || (RebateSceneEnum.PRIZE.equal(context.getRebateSceneCode())) && PromotionActTypeEnum.MATERIAL_REBATE.equal(context.getRebateActivityGiftExtract().getActType())) {
- handleFrame = materialRebateHandleFrameBuilder.build(context);
- } else {
- handleFrame = defaultRebateHandleFrameBuilder.build(context);
- }
- context.setHandleFrame(handleFrame);
- }
- /**
- * 获取通用资源数据
- *
- * @param context 上下文信息
- */
- private void fetchCommonResource(MultiRebateContext context) {
- }
- /**
- * 请求参数检查验证
- *
- * @param multiRebateCmd 请求参数指令
- */
- private void validateRebateCmdParams(MultiRebateCmdDTO multiRebateCmd) {
- SmcValidate.notNull(multiRebateCmd, "处理请求参数不能为空");
- }
- /**
- * <功能介绍><br>
- * <p>
- * <默认返利模块构建器>
- *
- * @author xy on 2022/6/14.
- * @see [相关类/方法](可选)
- * @since [产品/模块版本] (可选)
- */
- @Component
- public class DefaultRebateHandleFrameBuilder implements RebateHandleFrameBuilder {
- @Resource
- private MaterialMultiRebateHandle materialMultiRebateHandle;
- @Resource
- private YearMultiRebateHandle yearMultiRebateHandle;
- @Override
- public HandleFrame build(MultiRebateContext rebateContext) {
- return HandleFrame.instance()
- .addConfig(HandleFrame.HandleConfig.of(materialMultiRebateHandle.getIdentifier()))
- .addConfig(HandleFrame.HandleConfig.of(yearMultiRebateHandle.getIdentifier()));
- }
- }
- /**
- * <功能介绍><br>
- * <p>
- * <处理器构建业务执行>
- *
- * @author xy on 2022/6/14.
- * @see [相关类/方法](可选)
- * @since [产品/模块版本] (可选)
- */
- @SuppressWarnings("ALL")
- @Component
- public class SkeletonHandleBuildBiz {
- private static final YtLogger LOGGER = YtLoggerFactory.getLogger(SkeletonHandleBuildBiz.class);
- @Resource
- private AsdThreadPool rebateHandleThreadPool;
- /**
- * 构架构建并执行业务模块
- *
- * @param context 上下文信息
- *
- * @return 执行结果
- */
- public List<MultiRebateResult> buildHandles(MultiRebateContext context) {
- try {
- List<Callable<MultiRebateResult>> callableList = buildCallableList(context);
- List<Future<MultiRebateResult>> futures = rebateHandleThreadPool.invokeAll(callableList);
- if (CollectionUtils.isEmpty(futures)) {
- return Lists.newArrayList();
- }
- List<MultiRebateResult> rebateResults = Lists.newArrayList();
- for (Future<MultiRebateResult> future : futures) {
- rebateResults.add(future.get());
- }
- return rebateResults;
- } catch (InterruptedException | ExecutionException e) {
- LOGGER.src(REBATE_BIZ_FRAMEWORK).error("并行执行骨架业务处理异常,请求参数:{}", JSONObject.toJSONString(context), e);
- }
- return Lists.newArrayList();
- }
- private List<Callable<MultiRebateResult>> buildCallableList(MultiRebateContext context) {
- List<Callable<MultiRebateResult>> callableList = Lists.newArrayList();
- for (HandleFrame.HandleConfig handleConfig : context.getHandleFrame().getHandles()) {
- RebateHandle rebateHandle = getRebateHandle(handleConfig.getIdentifier());
- RebateMatcher rebateMatcher = getRebateMatcher(handleConfig.getMatcherIdentifier());
- RebateProcessor rebateProcessor = getRebateProcessor(handleConfig.getProcessorIdentifier());
- RebateExtension rebateExtension = getRebateExtension(handleConfig.getExtensionIdentifier());
- callableList.add(new Callable<MultiRebateResult>() {
- @Override
- public MultiRebateResult call() throws Exception {
- MultiRebateResult result = buildSingleHandle(context, rebateHandle, rebateProcessor, rebateMatcher, rebateExtension);
- return Optional.ofNullable(result).orElseGet(() ->
- MultiRebateResult.buildFailResult(handleConfig.getIdentifier() + "模块未返回结果")
- );
- }
- });
- }
- return callableList;
- }
- private MultiRebateResult buildSingleHandle(BaseRebateContext context,
- RebateHandle<BaseRebateHandleDTO, BaseRebateContext, MultiRebateResult> handle,
- RebateProcessor<BaseRebateHandleDTO, MultiRebateResult> processor,
- RebateMatcher<BaseRebateHandleDTO, BaseRebateContext> matcher,
- RebateExtension<BaseRebateHandleDTO> extension) {
- try {
- return (MultiRebateResult) handle.execute(context, matcher, processor, extension);
- } catch (DeadlockLoserDataAccessException e) {
- LOGGER.src(REBATE_BIZ_FRAMEWORK).warn("handle:{},执行处理异常.获取数据库锁失败", handle.getIdentifier(), e);
- return MultiRebateResult.buildFailResult("获取锁失败");
- } catch (DuplicateKeyException e) {
- LOGGER.src(REBATE_BIZ_FRAMEWORK).warn("handle:{},执行处理异常.唯一键冲突,重复处理", handle.getIdentifier(), e);
- return MultiRebateResult.buildFailResult(RebateResultEnum.NOT_CONFORM_RULE_ACT_FAIL);
- } catch (Throwable e) {
- LOGGER.src(REBATE_BIZ_FRAMEWORK).error("handle:{},执行处理异常.", handle.getIdentifier(), e);
- return MultiRebateResult.buildFailResult(e.getMessage());
- }
- }
- private RebateHandle getRebateHandle(String handleId) {
- return StringUtils.isBlank(handleId) ? null : RebateHandleIndex.getHandleIndex().get(handleId);
- }
- private RebateMatcher getRebateMatcher(String matcherId) {
- return StringUtils.isBlank(matcherId) ? null : RebateHandleIndex.getMatcherIndex().get(matcherId);
- }
- private RebateProcessor getRebateProcessor(String processorId) {
- return StringUtils.isBlank(processorId) ? null : RebateHandleIndex.getProcessorIndex().get(processorId);
- }
- private RebateExtension getRebateExtension(String extensionId) {
- return StringUtils.isBlank(extensionId) ? null : RebateHandleIndex.getExtensionIndex().get(extensionId);
- }
- }
- /**
- * <功能介绍><br>
- * <p>
- * <基础返利处理器>
- *
- * @author xy on 2022/6/14.
- * @see [相关类/方法](可选)
- * @since [产品/模块版本] (可选)
- */
- public interface RebateHandle<H extends BaseRebateHandleDTO, C extends BaseRebateContext, R> extends ClassNameIdentifiable {
- YtLogger LOGGER = YtLoggerFactory.getLogger(RebateHandle.class);
- @SuppressWarnings("AlibabaThreadShouldSetName")
- ExecutorService EXECUTOR = new ThreadPoolExecutor(20, 30, 0L,
- TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(2048));
- /**
- * 执行业务
- *
- * @param context 上下文参数
- * @param matcher 返利匹配器
- * @param processor 返利处理器
- * @param extension 扩展处理器
- *
- * @return 执行结果
- */
- default R execute(@NotNull C context, @Nullable RebateMatcher<H, C> matcher, @Nullable RebateProcessor<H, R> processor,
- RebateExtension<H> extension) {
- if (Objects.isNull(matcher) || Objects.isNull(processor)) {
- return null;
- }
- H matchHandleDTO = matcher.match(context);
- matchHandleDTO = post(matchHandleDTO, context);
- R process = processor.process(matchHandleDTO);
- Optional.ofNullable(matchHandleDTO).ifPresent(o -> Optional.ofNullable(extension).ifPresent(x -> {
- if (BooleanUtils.isTrue(o.getAsync())) {
- EXECUTOR.execute(() -> expand(x, o));
- } else {
- expand(x, o);
- }
- }));
- return process;
- }
- /**
- * 内部转换器
- *
- * @param handleDTO 数据模型
- * @param context 上下文参数
- *
- * @return 处理结果
- */
- default H post(H handleDTO, C context) {
- if (Objects.isNull(handleDTO)) {
- return null;
- }
- handleDTO.setRebateSceneCode(context.getRebateSceneCode());
- handleDTO.setAsync(context.getAsync());
- return handleDTO;
- }
- /**
- * 扩展逻辑执行
- *
- * @param extension 扩展器
- * @param handle 数据模型
- */
- default void expand(RebateExtension<H> extension, H handle) {
- try {
- extension.expand(handle);
- } catch (Throwable e) {
- LOGGER.src(REBATE_BIZ_FRAMEWORK).error("执行后置扩展器:{} 执行异常,参数信息:{}", extension.getIdentifier(), JSONObject.toJSONString(handle), e);
- }
- }
- }