当前位置:首页 > 科技  > 软件

转转游戏的账号订单流程重构之路

来源: 责编: 时间:2023-08-20 23:16:42 685观看
导读1、背景随着需求的不断迭代,项目代码的复杂度也会越来越高,“屎山”也一天一天慢慢的堆积起来,对于游戏业务的账号订单流程也是如此。游戏订单类型由原来的俩种增加到了现在的七种,早就已经到了需要重构的地步。但是由于

图片OEY28资讯网——每日最新资讯28at.com

既然有七种订单类型,这好办啊。可以采用策略+模板模式啊,一个抽象模板+七个子类就可以啦。但是后来仔细一想,如果将所有的处理逻辑都放在父类和子类当中,其实代码整体也显得十分臃肿。OEY28资讯网——每日最新资讯28at.com

为了想出更好的解决方案,于是对原有代码和业务流程进行了深入的梳理和总结,主要有以下几点:OEY28资讯网——每日最新资讯28at.com

  1. 所有订单流程都是在客服发货和自主发货基础上衍生出来的。
  2. 所有订单流程都包含下单、支付、上传账密、发货、确认收货等节点。
  3. 在这些节点里不同订单类型大多会有各自一些特定操作,但是这些操作其实并不属于订单的主流程。

通过以上分析,是不是可以将下单到确认收货作为一层,将不同订单类型的特定处理实现作为一层呢?这样不就将订单流程中各种特殊处理从订单主流程剥离开了吗,因此最终决定采用三层接口+策略模板的设计方案。OEY28资讯网——每日最新资讯28at.com

2.2 三层接口+策略模板模式

接口设计如下:OEY28资讯网——每日最新资讯28at.com

图片OEY28资讯网——每日最新资讯28at.com

  • 第一层接口

包含前端用户进行交互、处理mq消息以及给其它服务调用的接口。OEY28资讯网——每日最新资讯28at.com

  • 第二层接口

订单核心主流程能力接口。将下单、支付到确认收货等“不变”的基础能力提供给顶层接口调用,这层接口有自主发货流程和客服发货流程两个实现类。OEY28资讯网——每日最新资讯28at.com

public interface IGameAccountOrderDealProcess {    /**     * 处理下单未支付订单     */    int handlePlaceOrder(GameAccountOrderContext orderContext) throws Exception;    /**     * 处理支付成功订单     */    int handlePaySuccessOrder(GameAccountOrderContext orderContext) throws Exception;    /**     * 处理已发货订单     */    int handleDeliverOrder(GameAccountOrderContext orderContext) throws Exception;    /**     * 处理支付前取消订单     */    int handleCancelBeforePayOrder(GameAccountOrderContext orderContext) throws Exception;    /**     * 处理支付后取消订单     */    int handleCancelAfterPayOrder(GameAccountOrderContext orderContext) throws Exception;    /**     * 处理交易成功订单     */    int handleConfirmReceiptOrder(GameAccountOrderContext orderContext) throws Exception;    /**     * 账号交易窗数据     */    <T extends TradeFlowData> T getOrderTradeData(String logStr, Long orderId, Integer device, Long uid);    /**     * 上传账密     */    ZZOpenScfBaseResult<String> uploadAccountAndPwd(GameAccountSelfTrade.AccountPwdArg arg, long uid, String logStr, ServiceHeaderEntity header) throws Exception;    /**     * 发货     * @param orderContext     */    boolean deliverOrder(GameAccountOrderContext orderContext) throws Exception;    /**     * 订单确认收货     */    ZZOpenScfBaseResult<String> confirmReceiptOrder(GameAccountOrderContext orderContext, Long uid, boolean needCheckRisk) throws Exception;}
  • 第三层接口

各种订单类型的特殊处理,每一种订单模式都对应一个实现类。OEY28资讯网——每日最新资讯28at.com

public interface ITradeSelfHandler {    GameAccountTradeFlow.GameAccountTradeType getOrderTrade();    /*------------处理mq消息相关---------------*/    /**     *1.插入表之前设置客服和extendInfo     */    void fillExtraOrderInfoBeforeInsert(GameAccountOrderResultEntity orderEntity, GameAccountOrderContext orderContext);    /**     * 下单后处理     */    void handleAfterPlaceOrder(GameAccountOrderContext orderContext);    /**     * 支付前取消处理     */    void handleCancelBeforePay(GameAccountOrderContext orderContext);    /**     * 支付后取消处理     */    int handleCancelAfterPay(GameAccountOrderContext orderContext) throws Exception;    /**     * 支付后一些额外处理     */    int handleAfterPaySuccess(GameAccountOrderContext orderContext);    /**     * 确认收货处理     */    int handleAfterConfirmReceipt(GameAccountOrderContext orderContext) throws Exception;    /*---------------------------------*/    /**     * 获取提现时间     */    Date getWithDrawlTime();    /**     * 发送支付成功push     */    void orderAlreadyPayPushMsgNew(GameAccountOrderContext orderContext, Pair<String, String> jumpUrl);    /**     * 获取分帐账户、类别信息     */    List<AccountOrderSplitModel> getOrderSplitModelList(GameAccountOrderContext orderContext, OrderMaxSettleInfo settleInfo);    /**     * 定制各自spiUi     */    void buildOrderSpiUiData(GameAccountOrderContext orderContext, GameOrderSpiConfig bConfig, GameOrderSpiConfig sConfig, SpiUiData spiUiData) throws Exception;    /**     * 确认收货后一些处理     */    void otherOperationAfterReceipt(GameAccountOrderContext orderContext, Long uid);}

2.3 具体实现

  • 核心代码收拢到一个服务,相关接口进行聚合

原先在客服后台、定时任务、mq集群都有一些订单的操作,但是这些代码基本都是重复的,所以此次重构在订单核心服务中新增相应的订单操作功能,统一由其它服务进行RPC调用。OEY28资讯网——每日最新资讯28at.com

图片OEY28资讯网——每日最新资讯28at.com

将订单相关的接口、工具类集中到同一个包下,方便定位。OEY28资讯网——每日最新资讯28at.com

图片OEY28资讯网——每日最新资讯28at.com

  • 整体类图及设计原则图片
  1. 命名规范:类名、变量名、方法名尽量见名知义。
  2. 单一职责:各个模块各司其职,避免与其它模块过度耦合。
  3. 准备订单上下文,清除RPC重复调用问题。
//上下文实体public class GameAccountOrderContext {    private String logStr;    private Long orderId;    private Integer mqStatus;    private Order order;    private GameAccountOrderResultEntity accountOrderEntity;    private AccountOrderStatusEnum orderStatus;    private Boolean hasInsuranceService;//订单是否有保险    private GameAccountTradeFlow.GameAccountTradeType tradeType;    private GameAccountProductData accountProductData;    private ZZProduct product;    private ZZProductExt productExt;    private Map<String, String> extValueMap;    private AccountHelpSaleClue helpSaleClue;//帮卖线索    private DistributionShareInfoDTO distributionShareInfo;//分销信息    private ITradeSelfHandler tradeSelfHandler;    private Integer serviceUiStatus;//对应订单spi状态}//上下文准备GameAccountOrderContext orderContext = orderContextBuilder.buildAccountOrderContext(order, zzProduct, logStr);

3、上线保障

订单流程不管对于什么业务,基本都是最重要的一个环节,为了避免产生重大问题,需要做到以下两点:OEY28资讯网——每日最新资讯28at.com

  1. 严格保证线下测试的准确性。
  2. 出现线上问题,影响范围要尽可能小。

3.1 流程测试

根据账号订单流程的特点,在测试的时候遵循以下原则:OEY28资讯网——每日最新资讯28at.com

  • 订单流程正常跑通
  • 订单分帐正确
  • 订单保险正常
  • 各个节点与原来保持一致
  • 相关push、私信正常发送
  • 统计日志正常打印

对于每一种订单流程,同时进行新、老流程订单的测试。逐一对比新、老流程的买家侧和卖家侧各个流程节点的页面、按钮、跳转、push、私信等是否保持一致。OEY28资讯网——每日最新资讯28at.com

3.2 灰度策略

为了避免产生重大问题,上线后必须采取灰度策略,不然出了问题就可能就是事故了。本次采用的灰度策略是上线后按订单类型、订单量进行灰度,同时将灰度订单落表记录,配置如下:OEY28资讯网——每日最新资讯28at.com

[  {    "orderType": 6,//订单类型    "dayNum": 50,//每日灰度量    "isTotalGray": true//是否全量  }] /**  * 判断订单是否走新交易流程  */public boolean isNewOrderProcess(String logStr, GameAccountOrderContext orderContext) {        Long orderId = orderContext.getOrderId();        try {            if (gameGrayTestService.isNewTradeProcessOrder(orderId)){                return true;            }            GameAccountOrderResultEntity orderEntity = accountOrderManage.getGameAccountOrderEntity(orderId, logStr);            GameAccountTradeFlow.GameAccountTradeType orderTradeType = orderContext.getTradeType();            String orderRedisSet = String.format("account_order_gray_set_%s_%s", Objects.nonNull(orderEntity) ? orderEntity.getSelfType() : orderTradeType.getSelfType(), DateUtil.format(new Date(), "yyyy-MM-dd"));            if (ZZGameRedisUtil.sismember(orderRedisSet, orderId.toString())){                return true;            }            if (newAccountOrderTradeSwitch){                return true;            }            Optional<OrderGrayConfig> grayConfigOptional = grayConfigList.stream().filter(c->c.getOrderType() == orderTradeType.getSelfType()).findFirst();            if (grayConfigOptional.isPresent()){                OrderGrayConfig grayConfig = grayConfigOptional.get();                if (Objects.nonNull(grayConfig.getIsTotalGray()) && grayConfig.getIsTotalGray()){                    return true;                }                if (orderContext.getOrderStatus() != AccountOrderStatusEnum.place_order){//只处理新订单                    return false;                }                String dayNumKey = String.format(NEW_ORDER_PROCESS_GRAY_NUM, DateUtil.format(new Date(), "yyyy-MM-dd"), orderTradeType.getSelfType());                if (NumberUtils.toInt(ZZGameRedisUtil.get(dayNumKey)) < grayConfig.getDayNum()){                    int result = gameGrayTestService.insertNewTradeProcessOrder(orderId);                    log.info("{} desc=insert_gray_order_data orderId={} result={}", logStr, orderId, result);                    if (result > 0){                        ZZGameRedisUtil.increAndGet(dayNumKey, 1);                        ZZGameRedisUtil.expire(dayNumKey, 3600*24);                        ZZGameRedisUtil.sadd(orderRedisSet, orderId.toString());                        ZZGameRedisUtil.expire(orderRedisSet, 3600*24);                    }                    return result >= 0;                }                return false;            }        } catch (Exception e) {            log.error("{} desc=isNewOrderProcess_error orderId={}", orderContext.getLogStr(), orderContext.getOrderId(), e);        }        return false;}

3.3 异常机制

在一些重要的节点设置告警机制,比如上传账密、发货、提现等节点出现异常时会发送企业微信告警通知,可以第一时间关闭灰度,查找问题。OEY28资讯网——每日最新资讯28at.com

图片OEY28资讯网——每日最新资讯28at.com

不过对于分帐正确性保障这块只是通过测试确保正确,这种最好是可以接入中台的BCP(Business Check Platform)系统。它是一种标准化数据校对平台,支持标准化数据源接入,基于事件触发规则执行,进行业务数据校对,可以及时快速的发现业务异常数据并实时告警。OEY28资讯网——每日最新资讯28at.com

4 总结

在对订单流程进行重构之后,新增或修改某种订单模式,只需增改相应的订单类型处理类就可以了,也不用担心本次修改会影响到其它的订单模式,大大提高了开发效率。此外,重构代码可以帮助我们进一步深入了解整个业务流程,发现代码的坏味道,提升代码结构设计能力。OEY28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-6161-0.html转转游戏的账号订单流程重构之路

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 谷歌的Project IDX会扼杀其他应用程序开发框架吗?

下一篇: 基于模块联邦与大仓模式的商家巨石应用拆分实践

标签:
  • 热门焦点
  • Find N3入网:最高支持16+1TB

    OPPO将于近期登场的Find N3折叠屏目前已经正式入网,型号为PHN110。本次Find N3在外观方面相比前两代有很大的变化,不再是小号的横向折叠屏,而是跟别的厂商一样采用了较为常见的
  • 《英雄联盟》夏季赛总决赛今日开打!JDG对阵LNG首发名单来了 Knight:准备三连冠

    8月5日消息,今日17:00,《英雄联盟》2023LPL夏季赛总决赛将正式开打,由JDG对阵LNG。对两支队伍来说,这场比赛不仅要争夺夏季赛冠军,更要决定谁才是LPL赛区一
  • 太卷!Redmi MAX 100英寸电视便宜了:12999元买Redmi史上最大屏

    8月5日消息,从小米商城了解到,Redmi MAX 100英寸巨屏电视日前迎来官方优惠,到手价12999元,比发布价便宜了7000元,在大屏电视市场开卷。据了解,Redmi MAX 100
  • 线程通讯的三种方法!通俗易懂

    线程通信是指多个线程之间通过某种机制进行协调和交互,例如,线程等待和通知机制就是线程通讯的主要手段之一。 在 Java 中,线程等待和通知的实现手段有以下几种方式:Object 类下
  • 摸鱼心法第一章——和配置文件说拜拜

    为了能摸鱼我们团队做了容器化,但是带来的问题是服务配置文件很麻烦,然后大家在群里进行了“亲切友好”的沟通图片图片图片图片对比就对比,简单对比下独立配置中心和k8s作为配
  • 让我们一起聊聊文件的操作

    文件【1】文件是什么?文件是保存数据的地方,是数据源的一种,比如大家经常使用的word文档、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存数据,它既可以保
  • 使用AIGC工具提升安全工作效率

    在日常工作中,安全人员可能会涉及各种各样的安全任务,包括但不限于:开发某些安全工具的插件,满足自己特定的安全需求;自定义github搜索工具,快速查找所需的安全资料、漏洞poc、exp
  • 重估百度丨“晚熟”的百度云,能等到春天吗?

    &copy;自象限原创作者|程心排版|王喻可2016年7月13日,百度云计算战略发布会在北京举行,宣告着百度智能云的正式启程。彼时的会场座无虚席,甚至排队排到了门外,在场的所有人几乎都
  • 8月见!小米MIX Fold 3获得3C认证:支持67W快充

    这段时间以来,包括三星、一加、荣耀等等有不少品牌旗下的最新折叠屏旗舰都得到了不少爆料,而小米新一代折叠屏旗舰——小米MIX Fold 3此前也屡屡被传
Top