解密如何快速搭建一套虚拟商品交易系统,推荐这个神奇的开源项目

大家好,我是五阳,专注于分享电商交易系统设计~

最近我发现抖音上售卖的券包优惠力度很大,也囤了很多优惠券,我在想,能否模仿抖音券包,快速实现一个券包类虚拟商品售卖的交易系统呢?

说干就干,我借助于 memberclub 电商交易中台的SDK,用了将近1天的时间顺利完成开发。

关键需求分析

在抖音上券包的交易过程如下

  1. 进入商品页面,预览券包商品详情,可以看到抖音上的券包商品有有效期限制,过期后还支持过期退
  2. 在提单页面,可以加购商品,支持单商品多份数 购买。如果多次勾选会发现,部分商品有单用户购买限额。(非商品库存)
  3. 支付完成后,抖音会和三方系统或自身券系统交互,为用户履约发券
  4. 用户可以购买记录页,主动发起退款
  5. 进入售后预览 页面后,可看到退款金额。用户确定退款后,抖音会逆向履约,回收优惠券,最终原路退款。

抖音券包的交易流程

关键词

包括

  • 提单、用户限额、加购(多份数购买)、履约、逆向履约、售后预览、售后退款、过期退

为了叫着方便,模仿的抖音券包,我把它抖阳券包

仿抖音券包效果图

我花了1天时间,模仿抖音实现了券包交易过程。(五阳擅长后端,前端技术太差劲。后端这些功能有2个小时就搞定了,但是实现这些前端页面花了将近1天,然而效果图还是很low,意思一下,各位兄台多多包涵)

交易过程

  1. 进入主页后,可以看到券包商品、京东PLus会员商品列表。在主页可以选择直接提单购买,也可以加购物车,在购物车页面,可以选择多个商品一起提单支付和履约。
  2. 加购物车以后,点击结算,就会触发后端提单。示例中,我选择了两个券包商品,其中一个商品购买两份,模仿了抖音的多份数购买能力。选择提单并且模拟支付。(没有实现收银支付能力~ 坦白讲,五阳不擅长支付)
  3. 支付完成后,后端会履约券包,本次购买场景为:多商品、多份数购买。因为履约能力要支持多商品履约、多份数履约。
  4. 我在商品模型中,配置了券包购买限额为4,因此购买4份券包以后,再次提单会因购买限额不足而提单失败。
  5. 最后一步,在购买记录页,我点击退款按钮后,会触发售后预览,告诉我可以退的金额和赔付方式,点击确定后,系统会执行逆向履约,回收优惠券。同时要求系统要具备多商品、多份数场景的逆向履约能力。以上内容完成后,最终执行用户退款。

一点思考

虽然我不是抖音员工,但还是想说一句。抖音上有一点做的非常好,对三方系统的券包发起退款,抖音是极速垫付退款。在三方券可能还未回收之际,就先赔付用户。我猜测券包履约方为三方公司时,两套系统外加公网交互,数据不一致性问题会比较严重,如果先冻券,再退款,数据不一致时,极可能导致售后卡单,为了优化用户体验,就同时发起先退款和冻券。 虽然增加了资金损失风险,但是用户体验更好!这是一种平衡~

言归正传,看看,五阳1天时间如何实现的山寨版券包交易流程

实现思路

我认为,在系统设计阶段,正确的做法是先抽象业务共性,剥离业务特性,这样才能更好的抽象,构建的系统扩展性也更强。

从如上需求分析来看,抖音券包、京东会员等虚拟商品的交易过程具备较多的业务共性,在实现抖阳券包的交易能力时,我们应该首先抽象出这些业务共性,构建一个通用的虚拟商品交易中台。业务新接入时,为其分配新的业务身份,然后服用这部分通用能力,实现新业务的快速接入和未来快速扩展。

Memberclub项目提供了虚拟商品的交易解决方案,在各类购买场景下提供各类虚拟商品形态的履约及售后结算能力,它抽象了会员等虚拟商品的业务共性,同时对业务差异性预置了扩展点,通过插件方便扩展。

因此我会基于memberclub ,快速地搭建抖阳券包所需要的交易能力。

具体方案

定义业务身份

在 BizTypeEnum 业务身份枚举中定义,抖阳券包业务身份: 2

js 复制代码
/**
 * @author 掘金五阳
 */
public enum BizTypeEnum {

    DEFAULT(0, "default_biz"),
    DEMO_MEMBER(1, "demo_member"),
    VIDEO_MEMBER(3, "video_member"),
    MUSIC_MEMBER(4, "music_member"),
    DOUYIN_COUPON_PACKAGE(2, "douyin_coupon_package"),//douyin 优惠券包,支持过期退、多份数购买
    ;
}

商品模型定义

memberclub 中商品模型如下,包括业务身份,商品展示信息、商品结算信息、商品售卖信息、商品履约信息、商品限额信息、商品库存信息、及其他扩展字段。

新增抖阳券包时,商品模型无需新增或修改,仅需要再数据库中,创建券包商品数据。

js 复制代码
public class SkuInfoDO {

    private long skuId;

    private int bizType;

    private SkuViewInfo viewInfo = new SkuViewInfo();

    private SkuFinanceInfo financeInfo = new SkuFinanceInfo();

    private SkuSaleInfo saleInfo = new SkuSaleInfo();

    private SkuPerformConfigDO performConfig = new SkuPerformConfigDO();

    private SkuInventoryInfo inventoryInfo = new SkuInventoryInfo();

    private SkuRestrictInfo restrictInfo = new SkuRestrictInfo();

    private SkuExtra extra = new SkuExtra();

    private long utime;

    private long ctime;
}

新增商品券包数据如下。(下面的json被打平了,感兴趣可以在 JSON 可视化以后查看。)

perl 复制代码
{"skuId":200404,"buyCount":0,"bizType":2,"viewInfo":{"displayImage":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.alicdn.com%2Fbao%2Fuploaded%2Fi3%2F374544688%2FO1CN016Zx2lK1kV9QkrD6gW_%21%210-item_pic.jpg&refer=http%3A%2F%2Fimg.alicdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1742951021&t=9c87d26097559e952d220dff49a9d060","displayName":"15元混合券包","displayDesc":"无门槛组合券15元;有效期14天;过期退;有效期内限购4次","internalName":"15元混合券包","internalDesc":"无门槛组合券15元"},"financeInfo":{"contractorId":"438098434","settlePriceFen":900,"periodCycle":1,"financeProductType":1},"saleInfo":{"originPriceFen":1500,"salePriceFen":900},"performConfig":{"configs":[{"bizType":2,"rightType":1,"rightId":32424,"assetCount":1,"periodCount":14,"periodType":1,"cycle":1,"providerId":"1","grantInfo":{},"settleInfo":{"contractorId":"438098434","settlePriceFen":500,"financeAssetType":1,"financeable":true},"viewInfo":{"displayName":"5元立减券"},"saleInfo":{}},{"bizType":2,"rightType":1,"rightId":32423,"assetCount":1,"periodCount":14,"periodType":1,"cycle":1,"providerId":"1","grantInfo":{},"settleInfo":{"contractorId":"438098434","settlePriceFen":1000,"financeAssetType":1,"financeable":true},"viewInfo":{"displayName":"10元立减券"},"saleInfo":{}}]},"inventoryInfo":{"enable":false,"type":0},"restrictInfo":{"enable":true,"restrictItems":[{"periodType":"TOTAL","periodCount":14,"itemType":"TOTAL","userTypes":["USERID"],"total":4}]},"extra":{},"utime":0,"ctime":0}

以上商品数据定义在 这里,可以自行插入到数据库。

购买域实现

购买域主要包括三个业务接口,分别为提交订单、(未支付,主动取消)取消订单、售后取消订单。

java 复制代码
@ExtensionConfig(desc = "购买流程扩展点", type = ExtensionType.PURCHASE, must = true)
public interface PurchaseExtension extends BaseExtension {

    public void submit(PurchaseSubmitContext context);

    public void reverse(AfterSaleApplyContext context);

    public void cancel(PurchaseCancelContext context);
}

我们定义 DouyinPkgPurchaseExtension 实现该接口的三个业务方法,需要明确的是MemberClub在设计阶段就是定义为电商交易中台项目,所提供的能力均可轻松复用。复用方式主要包括两类:流程编排复用、扩展点定义复用。

从下面的类的提单方法可以看到,每个业务方法并非定制化实现,而是通过流程引擎编排流程节点,每个流程节点执行某一类业务逻辑。如提单流程中一共6个节点,分别为

  • 交易锁节点
  • 商品数据查询、校验和初始化节点
  • 提单上下文数据校验
  • 检查限额
  • 记录会员单
  • 通用订单中心提单(委托于订单中台接入收银支付等)

由于需求上不包含库存限制,因此在流程编排上去除了库存相关节点。如果抖音券包在提单流程上还有其他流程,可以新增流程节点,编排进抖音券包提单扩展点即可,不会影响到已接入的其他业务,最大程度实现了业务隔离。

js 复制代码
@ExtensionProvider(desc = "抖阳券包 购买提单扩展点", bizScenes = {
        @Route(bizType = BizTypeEnum.DOUYIN_COUPON_PACKAGE, scenes = {SceneEnum.HOMEPAGE_SUBMIT_SCENE})
})
public class DouyinPkgPurchaseExtension implements PurchaseExtension {

    private static FlowChain<PurchaseSubmitContext> submitChain = null;
    private static FlowChain<AfterSaleApplyContext> purchaseReverseChain = null;
    private static FlowChain<PurchaseCancelContext> purchaseCancelFlowChain = null;
    @Autowired
    private MemberOrderDomainService memberOrderDomainService;

    @PostConstruct
    public void init() {
        submitChain = FlowChain.newChain(PurchaseSubmitContext.class)
                .addNode(PurchaseSubmitLockFlow.class)
                .addNode(SkuInfoInitalSubmitFlow.class)
                .addNode(PurchaseSubmitCmdValidateFlow.class)
                .addNode(PurchaseUserQuotaFlow.class)                       //检查限额
                //.addNode(PurchaseValidateInventoryFlow.class)               //检查库存
                .addNode(MemberOrderSubmitFlow.class)                       // 会员提单
                //.addNode(PurchaseMarkNewMemberFlow.class)                   //新会员标记
                //.addNode(PurchaseOperateInventoryFlow.class)                //扣减库存
                .addNode(CommonOrderSubmitFlow.class)                       //订单系统提单
        ;

        purchaseReverseChain = FlowChain.newChain(AfterSaleApplyContext.class)
                //.addNode(PurchaseReverseNewMemberFlow.class)
                //.addNode(PurchaseReverseInventoryFlow.class)
                .addNode(PurchaseReverseMemberQuotaFlow.class)
        //
        ;

        purchaseCancelFlowChain = FlowChain.newChain(PurchaseCancelContext.class)
                .addNode(PurchaseCancelLockFlow.class)
                .addNode(PurchaseCancelOrderFlow.class)
                //.addNode(PurchaseCancelNewMemberFlow.class)
                .addNode(PurchaseCancelQuotaFlow.class)
        //.addNode(PurchaseCancelInventoryFlow.class)
        ;
    }

    @Override
    public void submit(PurchaseSubmitContext context) {
        submitChain.execute(context);
    }

    @Override
    public void reverse(AfterSaleApplyContext context) {
        purchaseReverseChain.execute(context);
    }

    @Override
    public void cancel(PurchaseCancelContext context) {
        MemberOrderDO memberOrder = memberOrderDomainService.
                getMemberOrderDO(context.getCmd().getUserId(), context.getCmd().getTradeId());
        context.setMemberOrder(memberOrder);

        purchaseCancelFlowChain.execute(context);
    }
}

履约域实现

商品履约主要在拆合单和权益履约部分需要扩展。

拆单部分

商品购买时因为履约、结算、售后等方面的诉求往往需要拆单履约。怎么理解呢?举例说明

  1. 用户购买两个商品,系统会拆分为两个子订单,分别进行履约。
  2. 用户购买1个商品,但是包含多类权益,需要不同的履约方进行履约,会将商品中包含的多个权益,拆分到履约项,分别进行履约。
  3. 用户购买年卡等商品,需要多期履约。系统会拆分为12期,分别进行履约。

可以看到拆单部分主要是对订单中的商品、商品中的权益进行拆分,制定履约计划,分别进行履约。 抖阳券包支持多份数购买,因此在拆单阶段会根据购买份数,拆单为多个履约项进行履约。

考虑到抖阳券包和默认券包的拆单能力诉求相同,我们直接在原来的扩展点上,添加抖阳券包业务身份,这样抖阳券包就能直接复用这个扩展点。

js 复制代码
@ExtensionConfig(desc = "履约拆单 扩展点", type = ExtensionType.PERFORM_MAIN, must = true)
public interface PerformSeparateOrderExtension extends BaseExtension {
    public void separateOrder(PerformContext context);
}
js 复制代码
@ExtensionProvider(desc = "默认 履约上下文构建", bizScenes = {
        @Route(bizType = BizTypeEnum.DEMO_MEMBER, scenes = {SceneEnum.SCENE_MONTH_CARD}),
        @Route(bizType = BizTypeEnum.DOUYIN_COUPON_PACKAGE, scenes = {SceneEnum.SCENE_MONTH_CARD}),
})
public class DemoMemberPerformSeparateOrderExtension implements PerformSeparateOrderExtension {

    FlowChain<PerformContext> performSeparateOrderChain = null;


    @Autowired
    private FlowChainService flowChainService;

    @PostConstruct
    public void run() throws Exception {
        performSeparateOrderChain = FlowChain.newChain(flowChainService, PerformContext.class)
                .addNode(InitialSkuPerformContextsFlow.class)
                .addNode(MutilBuyCountClonePerformItemFlow.class)
                //如果年卡周期是自然月,则可以在此处根据当前期数计算每期的天数
                .addNode(CalculateImmediatePerformItemPeriodFlow.class)//计算立即履约项 时间周期
                .addNode(CalculateOrderPeriodFlow.class)//计算订单整体有效期
                .addNode(PerformContextExtraInfoBuildFlow.class)// 构建扩展属性
        ;
    }

    @Override
    public void separateOrder(PerformContext context) {
        flowChainService.execute(performSeparateOrderChain, context);
    }
}

履约执行阶段扩展点

履约执行阶段的主要工作包括

  • 交易锁
  • 会员交易主单和子单模型记录和修改
  • 多商品履约能力
  • 创建和修改履约项
  • 调用履约方,发放权益。

由于抖阳券包需要支持过期退,因此在履约完成以后需要在任务表新增一条任务,系统每日定时执行已过期的任务。在券包过期后,系统自动发起售后。因此在执行扩展点新增了 MemberExpireRefundTaskCreatedFlow 流程节点,用来新增任务。

js 复制代码
@ExtensionProvider(desc = "抖阳券包执行履约扩展点", bizScenes = {
        @Route(bizType = BizTypeEnum.DOUYIN_COUPON_PACKAGE, scenes = {SceneEnum.SCENE_MONTH_CARD})//抖音券包月卡,多份数, 多商品
})
public class DouyinPkgPerformExecuteExtension implements PerformExecuteExtension {
    private FlowChain<PerformContext> flowChain;

    private FlowChain<PerformContext> subFlowChain;
    @Autowired
    private FlowChainService flowChainService;

    @PostConstruct
    public void init() {
        subFlowChain = FlowChain.newChain(flowChainService, PerformContext.class)
                .addNode(SingleSubOrderPerformFlow.class)
                .addNodeWithSubNodes(ImmediatePerformFlow.class, PerformItemContext.class,
                        // 构建 MemberPerformItem, 发放权益
                        ImmutableList.of(MemberPerformItemFlow.class, PerformItemGrantFlow.class));

        flowChain = FlowChain.newChain(flowChainService, PerformContext.class)
                .addNode(MemberResourcesLockFlow.class)
                .addNode(MemberOrderOnPerformSuccessFlow.class)
                .addNode(MemberPerformMessageFlow.class)
                .addNode(MemberExpireRefundTaskCreatedFlow.class)
                .addNodeWithSubNodes(MutilSubOrderPerformFlow.class, subFlowChain)
        ;
    }

    @Override
    public void execute(PerformContext context) {
        flowChainService.execute(flowChain, context);
    }
}

权益发放扩展点

抖阳券包在商品履约配置内容中,仅需要配置1张优惠券即可,毕竟券包售卖就是花钱买券包。 券包发放也比较简单,制定权益ID,绑定券模版ID、指定券包有效期和发券张数即可。(如果业务上还需要其他发放参数,在扩展字段中扩展即可)

我们定义了权益履约的通用SPI接口,履约方实现该接口(或者履约方定义接口,我们适配也行,只要保持接口协议稳定,不经常变化就行。)

less 复制代码
public interface AssetsFacadeSPI {

    @RequestMapping(method = RequestMethod.POST, value = "/items/grant")
    public GrantResponseDO grant(@RequestBody GrantRequestDO requestDO);

    @RequestMapping(method = RequestMethod.POST, value = "/items/fetch")
    public AssetFetchResponseDO fetch(@RequestBody AssetFetchRequestDO request);

    @RequestMapping(method = RequestMethod.POST, value = "/items/reverse")
    public AssetReverseResponseDO reverse(@RequestBody AssetReverseRequestDO request);
}

由于抖阳券包基于最基础的立减优惠券权益,系统已基于SPI接口实现,因此复用系统扩展点即可,无需新增。

售后域实现

售后主要包括三个业务方法,分别为

  • 售后预览:预览券包订单是否可以退,能退多少钱,如何赔付等。
  • 售后提单:提交售后单,并且发起逆向履约、逆向提单、退款等流程。
  • 过期退:一个特殊售后渠道,系统需要基于定时任务,为过期的订单自动发起退款!

售后预览

售后预览阶段主要是检查用户订单数据合法性,如状态机校验、有效期校验、券包使用状态校验。抖阳券包计算券包是否可退时,基于券使用状态,对每张券分摊的实付金额进行加和,计算未使用券包的金额,如果金额大于0,则可以退,如果金额为0,则不可退。

以下是售后预览阶段的扩展点。

js 复制代码
@ExtensionProvider(desc = "抖音券包售后预览扩展点", bizScenes = {
        @Route(bizType = BizTypeEnum.DOUYIN_COUPON_PACKAGE, scenes = {SceneEnum.SCENE_AFTERSALE_MONTH_CARD})
})
public class DouyinPkgAftersalePreviewExtension implements AftersalePreviewExtension {

    private FlowChain<AftersalePreviewContext> previewChain = null;

    private FlowChain<AftersalePreviewContext> subPreviewChain = null;

    @Autowired
    private FlowChainService flowChainService;

    @PostConstruct
    public void init() {
        subPreviewChain = FlowChain.newChain(AftersalePreviewContext.class)
                .addNode(RealtimeCalculateUsageAmountFlow.class)            //实时计算使用类型
                .addNode(OverallCheckUsageFlow.class)               //完全检查使用类型
                .addNode(CalculateRefundWayFlow.class)                  //计算赔付类型
                .addNode(GenerateAftersalePlanDigestFlow.class)         //生成售后计划摘要
        ;

        previewChain = FlowChain.newChain(AftersalePreviewContext.class)
                .addNode(AftersalePreviewDegradeFlow.class)
                // TODO: 2025/1/1  //增加售后单 进行中校验,当前存在生效中受理单,不允许预览(数据处于不一致状态,无法获得准确的预览结果),返回特殊错误码
                .addNode(AftersaleStatusCheckFlow.class)
                .addNode(AftersaleGetAndCheckPeriodFlow.class)
                .addNode(GetAndCheckAftersaleTimesFlow.class)
                .addNodeWithSubNodes(MutilSubOrderPreviewFlow.class, subPreviewChain)
        ;
    }

    @Override
    public void preview(AftersalePreviewContext context) {
        flowChainService.execute(previewChain, context);
    }
}

售后受理

售后受理阶段需要再次检查券包订单是否可退,是否和售后预览结果一致。售后受理流程主要通过售后单状态机进行驱动,包括调用履约域逆向履约方法、提单域逆向取消订单、和调用订单退款。

js 复制代码
@ExtensionProvider(desc = "示例会员售后受理扩展点", bizScenes = {
        @Route(bizType = BizTypeEnum.DOUYIN_COUPON_PACKAGE, scenes = {SceneEnum.SCENE_AFTERSALE_MONTH_CARD})
})
public class DouyinPkgAfterSaleApplyExtension implements AfterSaleApplyExtension {


    FlowChain<AfterSaleApplyContext> applyFlowChain = null;

    FlowChain<AfterSaleApplyContext> checkFlowChain = null;

    FlowChain<AfterSaleApplyContext> doApplyFlowChain = null;

    @Autowired
    private FlowChainService flowChainService;

    @PostConstruct
    public void init() {
        applyFlowChain = FlowChain.newChain(flowChainService, AfterSaleApplyContext.class)
                .addNode(AftersaleApplyLockFlow.class)     //加锁
                .addNode(AftersaleApplyPreviewFlow.class)       //售后预览
                .addNode(AfterSalePlanDigestCheckFlow.class)    //校验售后计划摘要
                .addNode(AftersaleGenerateOrderFlow.class)      //生成售后单
                .addNode(AftersaleDoApplyFlow.class)
        ;

        doApplyFlowChain = FlowChain.newChain(flowChainService, AfterSaleApplyContext.class)
                .addNode(AftersaleOrderDomainFlow.class)                
                .addNode(MemberOrderRefundSuccessFlow.class) //售后成功后, 更新主单子单的状态为成功
                .addNode(AftersaleAsyncRollbackFlow.class)   // 失败异步回滚
                .addNode(AftersaleReversePerformFlow.class)  //逆向履约
                .addNode(AftersaleReversePurchaseFlow.class) //逆向取消订单
                .addNode(AftersaleRefundOrderFlow.class)     //退款
        //.addNode()
        ;


    }

    @Override
    public void apply(AfterSaleApplyContext context) {
        flowChainService.execute(applyFlowChain, context);
    }

    @Override
    public void doApply(AfterSaleApplyContext context) {
        flowChainService.execute(doApplyFlowChain, context);
    }

    @Override
    public void customBuildAftersaleOrder(AfterSaleApplyContext context, AftersaleOrderDO aftersaleOrderDO) {

    }
}

过期退

过期退流程依赖任务定时触发能力,memberclub 定义了 任务触发扩展点,每个业务身份和任务类型均可以自行扩展过期任务如何触发。

如下扩展点,分为两个流程,其一为触发流程,主要功能是根据数据库分库分表,并发的扫描过期的目标任务,扫描出以后,需要调用 执行流程,每个执行流程仅处理1个过期任务。执行流程也支持编排,如过期退执行流程,编排了两个节点,分别为

  1. 节点1:任务开始和终止时修改 任务状态
  2. 节点2: 调用售后域业务接口,执行过期退。
csharp 复制代码
public abstract class DefaultExpireRefundTriggerExtension implements OnceTaskTriggerExtension {
    private FlowChain<OnceTaskTriggerContext> triggerFlowChain = null;

    private FlowChain<OnceTaskExecuteContext> executelowChain = null;

    @PostConstruct
    public void init() {
        triggerFlowChain = FlowChain.newChain(OnceTaskTriggerContext.class)
                .addNode(OnceTaskSeprateFlow.class)
                .addNodeWithSubNodes(OnceTaskConcurrentTriggerFlow.class, OnceTaskTriggerJobContext.class,
                        ImmutableList.of(OnceTaskForceRouterFlow.class, OnceTaskScanDataFlow.class)
                )
                .addNode(OnceTaskTriggerMonitorFlow.class)
        ;

        executelowChain = FlowChain.newChain(OnceTaskExecuteContext.class)
                .addNode(OnceTaskRepositoryFlow.class)
                .addNode(ExpiredRefundOnceTaskExecuteFlow.class)
        ;
    }


    @Override
    public void trigger(OnceTaskTriggerContext context) {
        triggerFlowChain.execute(context);
    }

    @Override
    public void execute(OnceTaskExecuteContext context) {
        executelowChain.execute(context);
    }
}

模拟客户端页面

客户端部分在uniapp 插件市场,找到了一个开源模版,其中包括商品列表页和购物车页面,我修改了和后端接口交互部分,一共以下几个后端接口。我自己新增了购买记录页。

  • 首页可查看商品列表,因此提供商品列表页接口
  • 提单接口
  • 模拟支付接口
  • 购买记录、订单列表页
  • 售后预览接口
  • 售后提单受理接口

技术栈包括

  • uniapp + vue
  • element-ui
  • axios

写在最后

以上后端部分大概花了2个小时就能完成,主要原因是我基于memberclub 进行二次开发,它提供了虚拟商品交易提单、履约、售后和结算的 基础SDK,并且提供了流程引擎扩展点引擎能力,可以非常快速的支持新增业务身份进行扩展。

如果是从0到1开发,别说2小时,两天甚至两周也不一定能开发完~

memberclub开源地放这里了,想学习电商交易系统设计的可以看看。

Giteegitee.com/juejinwuyan...

GitHub : github.com/juejin-wuya...

如何初始化环境

memberclub 在standalone模式下无需任何中间件即可启动,在集成测试环境默认依赖 mysql/redis/apollo/rabbitmq 等中间件。所以如果仅学习使用,只需要启动memberclub服务即可!

独立启动

cd bin && ./starter.sh -e ut

然后 git clone 下载memberclub H5项目,地址在 gitee.com/juejinwuyan...

下载完成后,需要下载 HBuilderX IDE 启动H5项目。这样就可以了

相关推荐
LUCIAZZZ3 小时前
TCP基本入门-简单认识一下什么是TCP
java·网络·后端·网络协议·tcp/ip·计算机网络·spring
_未知_开摆4 小时前
2020年蓝桥杯Java B组第二场题目+部分个人解析
java·经验分享·后端·程序人生·蓝桥杯
m0_748234524 小时前
Spring Boot整合WebSocket
spring boot·后端·websocket
m0_748232394 小时前
SpringBoot Maven 项目 pom 中的 plugin 插件用法整理
spring boot·后端·maven
Asthenia04125 小时前
深入解析消息持久化实现机制:基于 `LocalMqBrokerPersist` 的简化实现
后端
xidianhuihui5 小时前
go如何排查某个依赖是哪里引入的
开发语言·后端·golang
姜来可期5 小时前
【Golang】go语言异常处理快速学习
开发语言·笔记·后端·学习·golang
Toormi6 小时前
Go 1.24版本在性能方面有哪些提升?
开发语言·后端·golang
飘零未归人6 小时前
SpringBoot 整合mongoDB并自定义连接池,实现多数据源配置
spring boot·后端·mongodb