GVKun编程网logo

36、生鲜电商平台-积分,优惠券,会员折扣,签到、预售、拼团、砍价、秒杀及抽奖等促销模块架构设计

16

此处将为大家介绍关于36、生鲜电商平台-积分,优惠券,会员折扣,签到、预售、拼团、砍价、秒杀及抽奖等促销模块架构设计的详细内容,此外,我们还将为您介绍关于10、生鲜电商平台-财务系统模块的设计与架构、

此处将为大家介绍关于36、生鲜电商平台-积分,优惠券,会员折扣,签到、预售、拼团、砍价、秒杀及抽奖等促销模块架构设计的详细内容,此外,我们还将为您介绍关于10、生鲜电商平台-财务系统模块的设计与架构、13、生鲜电商平台-订单抽成模块的设计与架构、14、生鲜电商平台-搜索模块的设计与架构、15、生鲜电商平台-售后模块的设计与架构的有用信息。

本文目录一览:

36、生鲜电商平台-积分,优惠券,会员折扣,签到、预售、拼团、砍价、秒杀及抽奖等促销模块架构设计

36、生鲜电商平台-积分,优惠券,会员折扣,签到、预售、拼团、砍价、秒杀及抽奖等促销模块架构设计

说明:本标题列举了所有目前社会上常见的促销方案,目前贴出实际的业务运营手段以及架构设计,包括业务说明,仅供参考

 

促销体系

1.1促销体系

在电商和O2O领域,促销是运营人员的一个主要的让利行为,同时促销活动期间的购买量也较之普通商品更高,不同的阶段,对于促销的要求也是不同的。

促销实质上是一种沟通活动,即营销者(信息提供者或发送者)发出作为刺激消费的各种信息,把信息传递到一个或更多的目标对象(即信息接受者,如听众、观众、读者、消费者或用户等),以影响其态度和行为。

商城促销活动的流程概述(不含优惠券):

  1. 在平台后台创建促销活动
  2. 选择促销方式,对应编辑该促销方式的设置项,选择参与活动的商品,可选择普通购买的单纯用货币支付的普通商品
  3. 设置具体的促销活动规则和投放设置
  4. 用户在商城查看商品,可看到由平台后台发布的优惠信息
  5. 将商品加入购物车,购物车将体现享受满减和直减后的优惠价格,最终的购买订单的实付金额为享受促销活动后的价格。
  6. 可在后台查看到已创建的活动以及活动的效果,关于用户购买订单相关的数据也将展示其享受到的优惠信息。

1.2促销系统

将模块拆分,主要分为三部分:

  1. 促销活动:活动投放的设置管理,负责提供活动方式和商品内容
  2. 促销规则:发布促销活动时选择,负责提供促销玩法,例如限时折扣、满额减等
  3. 优惠券:提供一种相对独立的促销形式

从大的维度来看,优惠券也属于促销的一种方式,在促销规则也和优惠劵的使用有一定关联。这里我们把优惠券也归类到促销系统中,关于优惠券业务将在下次进行迭代

1.3促销活动

活动状态:

  • 未开始,还未到活动开始时间,此状态的活动可进行编辑和的删除,删除活动为逻辑删除
  • 活动中,正在进行中的活动,此状态的活动不能编辑,只能提前结束,结束之后的活动变更为已失效的状态
  • 已失效,已经过了活动结束时间,被删除的以及被取消的活动

从促销类型分为:

  1. 直减类:限时折扣、新用户专享等
  2. 满减类:满额减、满额折、满件减、满件折、满件免等
  3. 赠券类:买单赠券、免费领券等(下次迭代)
  4. 组合优惠类(暂不考虑):套餐
  5. 送赠品类(暂不考虑):满赠
  6. 换购类(暂不考虑):加价购
  7. 预订类(暂不考虑):预售价

直减类(优先实现):

主要设置项包括:

  1. 活动名称:方便运营人员识别活动
  2. 促销方式:可选限时折扣、新用户专享
  3. 商品选择:参加促销的商品以及促销的价格(只选择单纯用货币购买的普通商品)
  4. 投放时间选择:选择开始时间和结束时间
  5. 打标签:商品打促销标识,前端显示促销标签,可选择现有标签或者选择自定义新增
  6. 目标群体(不可编辑): 针对全部用户(限时折扣)、未下单过的新用户(新用户专享)

满减类:

主要设置项包括:

  1. 活动名称:方便运营人员识别活动
  2. 促销方式:满额减、满额折、满件减、满件折、满件免件
  3. 商品选择:参加促销的商品((只选择单纯用货币购买的实际商品,可选择全场、按品类、或者自主选择多个商品,由于虚拟商品不能加入购物车,所以虚拟商品的满减类虽然可以添加多个SKU,但是最终享受该优惠的虚拟商品订单只会包含一个SKU)
  4. 金额/件数设置:满XX减(折、免)XX,可设置多层级的满减活动(支持两种货币类型的情况)
  5. 投放时间选择:选择开始时间和结束时间
  6. 打标签:商品打促销标识,前端显示促销标签,可选择现有标签或者选择自定义新增
  7. 目标群体: 针对全部用户、未下单过的新用户

二、促销业务逻辑

2.1创建多个活动

方案1:实现此方案,一个商品只能关联到一个正在进行的活动,已经被添加至一个活动的时候,可以先解绑再将其添加至另一个活动

方案2:同类型促销可创建多个活动,同个商品可参与多个同类型的活动,但是一个商品不能同时被添加至多个活动时间重叠的活动(考虑这样处理逻辑会比较清晰)

一笔订单不能同时享受两个及以上相同类型的优惠。(这里的类型指的是直减类、满减类)

2.2订单金额计算

方案1:本次实现此方案,一个用户一笔订单只能享受一种优惠,如果一笔订单有多个优惠活动,用户可进行选择其中一个。

方案2:促销方式有很多,针对商品或订单的满减、折扣、优惠券等,对这些促销类型进行任意组合,将会有非常多样化的场景,为了防止系统发生重叠甚至冲突的情况,从整体上设计促销逻辑才能保证各子系统流程顺畅流转。

将促销视为订单金额的变化,将促销活动区分为三种类型:改商品价格、改商品小计价格、改订单价格,无论什么促销都可以描述成改价格。

判断条件:

  1. 上面的流程图,是否可享受的判断条件是只要有优惠活动就享受
  2. (讨论是否在这一阶段加上多活动时的控制)通用设置或者是针对于活动的设置,同一笔订单是否可以同时享受多个不同类型的促销,即同时可以享受修改商品的优惠(直减类),修改商品小计的集合(满减类)或修改订单的价格(优惠券)

具体例子:(京东)

  1. 享受商品价格的活动,这里是秒杀价单品价格24.9元
  2. 计算商品小计之后,引入针对指定商品的价格,满2件总价打5折,24.9*28件*0.5=348.6元
  3. 累计商品小计价格之后,介入针对于订单的促销:优惠券

优惠券为全品类满200减10,满足条件,即最终订单价格(免邮)为348.9-10=338.6元

所以最终这笔订单先后享受了三重优惠。

2.2.1修改商品价格

一笔订单被提交时,系统必然首先查找出订单中的所有商品,并判断此商品是否具备特殊价格(促销价格)。如果没有,则取正常的商品零售价;如果有,则取当前商品所处促销活动价格。

直减类:限时折扣、新用户专享等

遵循促销核心原则:同类型促销通过同一实体进行互斥、不同类型促销可以叠加。这里的实体指的是商品,类型指的是前文说到的值减类、满减类、赠券类,下文也是,不再说明。

所以这条原则也可以表述为:不同商品可以享受相同的(限时折扣、新用户专享)活动;但是相同商品进行互斥,同一个商品不可以同时享受同一类型的活动,例如:同一个SKU不可以同时享受(限时折扣、新用户专享)活动。

2.2.2修改商品小计的集合价格

当系统完成了对商品价格的查找之后,就需要将查找出的商品价格分别乘以订单中的每个商品数量,从而计算出每个商品的小计金额。当系统计算出所有商品的小计金额之后,这时候,就可以介入一些促销活动,例如指定商品的:满额减、满额折等。

满减类:满额减、满额折、满件减、满件折、满件免件

例如:全场生鲜类,满100减10,满200减30;就是第二种类型活动,因为它是针对某种/某类商品的小计金额来匹配满足哪种类型的活动,并在满足活动的商品小计金额基础上进行减、折、免。

同类型通过实体进行互斥、不同类型可以相互叠加。此处的实体是商品,所以这对第二种类型的促销活动,我们就可以得出以下结论:

同一个商品,不能同时享受指定商品的(满减、满赠、折扣 等)活动,不同的商品没有限制。

例如:SKUA 不能同时享受多个满减、满赠、折扣;但是SKUA 享受满减,SKUB享受满赠 这种是被允许的。

而不同类型可以相互叠加,也就是说,同一个商品虽然不能同时享受同种类型的活动,但是却可以同时享受不同类型的促销活动,例如:SKUA 可以同时享受限时折扣和指定SKUA的满减活动,也就是折上折。

2.2.3修改订单金额

当系统完成了对商品小计金额的计算之后,就会将所有优惠后的商品小计金额进行叠加,生成一个初步的订单总金额,当系统得到初步的订单总金额之后,又可以介入一些促销活动,例如指定订单的:优惠券等。

针对订单类的:优惠券(下次迭代)

例如:京东招牌活动全场满88包邮;就是典型的第三种类型营销活动,因为它是针对订单的总金额来匹配满足那种类型的活动,并在满足活动的订单金额基础上进行减、折、优惠券和包邮券等。

同类型通过实体进行互斥、不同类型可以相互叠加,此处的实体就是订单。

所以,针对第三种类型的促销活动,我们就可以得出以下结论:同一笔订单只能使用一张针对于订单的优惠券。

 

三、功能需求清单(促销活动)

 

 

转载自-- https://www.cnblogs.com/jurendage/p/9165594.html

10、生鲜电商平台-财务系统模块的设计与架构

10、生鲜电商平台-财务系统模块的设计与架构

前言:任何一个平台也好,系统也好,挣钱养活团队这个是无可厚非的,那么对于一个生鲜B2B平台盈利模式( 查看:http://www.cnblogs.com/jurendage/p/9016411.html)而言,

其中财务模块无论是对于买家而言还是卖家而言都至关重要,老百姓对钱的看重是没有经历的人想不到的,一句话说清楚了:一分钱也不能少。

 

买家或者卖家对财务模块的要求很简单:

                              1. 账清楚明白。

                              2. 消费清清楚楚。

                              3. 计算准确无误。

 

对平台而言:财务的要求是每笔资金的输入与输出,有理有据,真真实实。

 

我们分两个模块来讨论,买家与卖家我们称为客户财务模块,平台我们称为公司财务模块

 

1. 客户财务模块

      1.1 买家需要查看任何一天的消费记录。数据按照时间的倒叙排列,根据时间进入某天的消费记录,需要查看当天的历史订单数据。

            业务中按照时间来分组,数据是来源于订单表:

            相关业务核心代码如下:

             

复制代码
/**
     * 订单列表
     * 
     * @description 1.用户的所有订单列表。 2.支付状态 3.交易状态.
     * @param request
     * @param response
     * @param orderStatus
     *            0 待支付 1待送达 2已完成
     * @return
     */
    @RequestMapping(value = "/order/list", method = { RequestMethod.GET, RequestMethod.POST })
    public JsonResult buyerOrderList(HttpServletRequest request, HttpServletResponse response, Long buyerId,
            int orderStatus) {

        List<OrderListVo> listVo = new ArrayList<OrderListVo>();

        // 需要顺序
        Map<String, List<OrderInfoVo>> resultMap = new TreeMap<String, List<OrderInfoVo>>(new Comparator<String>() {
            public int compare(String obj1, String obj2) {
                // 降序排序
                return obj2.compareTo(obj1);
            }
        });

        try {
            List<OrderInfo> orderList = orderInfoService.getAllOrderInfo(buyerId, orderStatus);

            // 判断是否有订单
            if (CollectionUtils.isEmpty(orderList)) {
                return new JsonResult(JsonResultCode.SUCCESS, "订单查询成功", listVo);
            }

            // 组装Map对象
            for (OrderInfo order : orderList) {
                List<OrderInfoVo> resultList = new ArrayList<OrderInfoVo>();

                OrderInfoVo orderInfoVo = transform(order);

                String time = DateUtil.dateToString(order.getCreateTime(), "yyyy-MM-dd");

                if (resultMap.get(time) == null) {
                    resultList.add(orderInfoVo);
                    resultMap.put(time, resultList);
                } else {
                    List<OrderInfoVo> mapList = resultMap.get(time);
                    mapList.add(orderInfoVo);
                    resultMap.put(time, mapList);
                }
            }

            // 组装VO
            Set<String> keys = resultMap.keySet();
            for (String data : keys) {
                OrderListVo vo = new OrderListVo();
                vo.setDate(data);
                vo.setList(resultMap.get(data));
                listVo.add(vo);
            }
            return new JsonResult(JsonResultCode.SUCCESS, "订单查询成功", listVo);
        } catch (Exception ex) {
            logger.error("[OrderController][buyerOrderList] exception :", ex);
            return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试", "");
        }
    }
复制代码

说明:买家而言其实就是待支付,已经支付等订单的明细查询,根据时间进行分组,根据时间(按照天来计算,查看自己的明细。),然后查询出整个订单明细即可。

 

相关运营截图如下:

               

 

 

    1.2  对于卖家而言,他肯定想知道每天具体卖了多少东西,多少钱,但是不是针对某一个买家而言,是针对整个统计而言。

          比如:他想知道我今天卖了土豆多少共多少斤,白菜多少斤,萝卜多少斤,花菜多少斤等等,同时他自己会算出今天的利润多少。

        补充说明:其实系统是可以算出来今天他挣钱多少的,但是客户很讨厌输入进货价,相当于把自己的“秘密”出卖了一样,整个系统架构中

先前是有的,但是实际运营发现,不是我们的那个样子,最终是去掉了。

 

          相关的业务核心代码如下:(数据也是来源于订单明细表中)

           

订单主表的列表:

 

复制代码
/**
     * 查询买家需要确认的订单列表
     */
    @RequestMapping(value = "/order/list", method = { RequestMethod.GET, RequestMethod.POST })
    public JsonResult orderList(HttpServletRequest request, HttpServletResponse response,Integer type,Long saleId) {
        try {
            List<OrderVo> orderList = orderService.getOrderList(type, saleId);
            return new JsonResult(JsonResultCode.SUCCESS, "查询信息成功", orderList);
        } catch (Exception ex) {
            logger.error("[OrderController][orderList] exception :", ex);
            return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试", "");
        }
    }
复制代码

 

根据订单号获取订单明细:

     

复制代码
/**
     * 查询买家需要确认的订单列表
     */
    @RequestMapping(value = "/order/detail", method = { RequestMethod.GET, RequestMethod.POST })
    public JsonResult getOrderDetail(HttpServletRequest request, HttpServletResponse response,Long orderId) {
        try {
            OrderDetailVo odv = orderService.getOrderDetail(orderId);
            return new JsonResult(JsonResultCode.SUCCESS, "查询信息成功", odv);
        } catch (Exception ex) {
            logger.error("[OrderController][getOrderDetail] exception :", ex);
            return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试", "");
        }
    }
复制代码

 

订单明细VO:对象:

    

复制代码
/**
 * 订单详情VO
 */
public class OrderDetailVo implements java.io.Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 订单主表id,order_info表的order_id
     */
    private Long orderId;
    /**
     * 唯一订单号
     */
    private String orderNumber;
    /**
     * 买家ID
     */
    private Long buyerId;
    /**
     * 买家店铺名称
     */
    private String buyerName;
    /**
     * 买家店铺图片
     */
    private String buyerLogo;
    /**
     * 买家地址
     */
    private String buyerAddress;
    /**
     * 买家姓名
     */
    private String bossName;
    /**
     * 买家手机
     */
    private String bossTel;
    /**
     * 收货人的最佳送货时间
     */
    private String bestTime;
    /**
     * 订单创建时间
     */
    private String createTime;
    /**
     * 商品列表
     */
    private List<OrderGoodsVo> goodsList;
    
    public Long getOrderId() {
        return orderId;
    }
    public void setOrderId(Long orderId) {
        this.orderId = orderId;
    }
    public String getOrderNumber() {
        return orderNumber;
    }
    public void setOrderNumber(String orderNumber) {
        this.orderNumber = orderNumber;
    }
    public Long getBuyerId() {
        return buyerId;
    }
    public void setBuyerId(Long buyerId) {
        this.buyerId = buyerId;
    }
    public String getBuyerName() {
        return buyerName;
    }
    public void setBuyerName(String buyerName) {
        this.buyerName = buyerName;
    }
    public String getBuyerLogo() {
        return buyerLogo;
    }
    public void setBuyerLogo(String buyerLogo) {
        this.buyerLogo = buyerLogo;
    }
    public String getBuyerAddress() {
        return buyerAddress;
    }
    public void setBuyerAddress(String buyerAddress) {
        this.buyerAddress = buyerAddress;
    }
    public String getBossName() {
        return bossName;
    }
    public void setBossName(String bossName) {
        this.bossName = bossName;
    }
    public String getBossTel() {
        return bossTel;
    }
    public void setBossTel(String bossTel) {
        this.bossTel = bossTel;
    }
    public String getBestTime() {
        return bestTime;
    }
    public void setBestTime(String bestTime) {
        this.bestTime = bestTime;
    }
    public String getCreateTime() {
        return createTime;
    }
    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }
    public List<OrderGoodsVo> getGoodsList() {
        return goodsList;
    }
    public void setGoodsList(List<OrderGoodsVo> goodsList) {
        this.goodsList = goodsList;
    }
}
复制代码

 

相关SQL核心:

           

复制代码
<!-- 订单详情 -->
  <select id="getOrderDetail" resultMap="orderDetailMap">
      select 
        o.order_id,o.order_number,o.buyer_id,b.buyer_name,b.buyer_logo,b.buyer_address,b.boss_name,b.boss_tel,
        date_format(o.best_time,''%Y-%m-%d %H:%i'') as best_time,date_format(o.create_time,''%Y-%m-%d %H:%i'') as create_time,
        oi.item_id,oi.remark,g.goods_id,g.goods_name,g.goods_brand,g.goods_label,
        gf.format_id,gf.format_name,u.unit_id,u.unit_name,gf.format_num,
        pm.method_id,pm.method_name,oi.goods_price,oi.goods_number,oi.goods_amount,
        gp.pic_id,gp.pic_url,gp.is_main,gp.pic_seq
     from order_item oi
        inner join order_info o on o.order_id=oi.order_id
        inner join buyer b on b.buyer_id=o.buyer_id
        inner join goods_format gf on gf.format_id=oi.format_id
        inner join goods g on gf.goods_id=g.goods_id
        inner join unit u on gf.unit_id=u.unit_id
        left join process_method pm on pm.method_id=oi.method_id 
        left join goods_picture gp on g.goods_id=gp.goods_id
     where oi.order_id=#{orderId} 
     order by gp.is_main desc
  </select>
复制代码

 

疑问一:为什么会贴出如此多的代码吗?尤其是SQL? 

理由只有一点:的确这块比较复杂,很多都是基于SQL语句的查询,需要SQL功底。

 

相关的业务运营截图如下:

 

 

 

 

 

 

 

下面的核心的历史收益这块,卖家可以扔掉自己的手抄本了。根据这个平台就可以方便的明细的账单记录

 

 

 

补充说明:数据报表有金额,有数据,有统计,有分析,包括以后的折线图,柱状图等等报表分析,让用户明确的知道今天挣钱多少,明天预警备货多少,一切清楚明了。

 

总结:我的经验是所有的人心与人性都是相同的,你需要功能的客户其实也想看到,对于这种模式的采用,我们是经历了很多都经验后才总结出来的,

          看起来很简单,其实背后的努力不简单。一起努力做好生鲜电商。

 

转载自-- https://www.cnblogs.com/jurendage/p/9049318.html

13、生鲜电商平台-订单抽成模块的设计与架构

13、生鲜电商平台-订单抽成模块的设计与架构

说明:订单抽成指的是向卖家收取相应的信息服务费.(目前市场上有两种抽成方式,一种是按照总额的抽成比率,另外一种是按照订单明细的抽成比率)

           由于生鲜电商的垂直领域的特殊性质,总额抽成不切合实际,所以按照订单的明细抽成。

 

1. 订单抽成,是按照一个区的维度,以及菜品的二级分类类抽点的。

 

   举例说明:比如武汉光谷区,佛祖岭区,虽然都是属于东湖高新,但是光谷区的物价以及消费水平肯定是高于佛祖岭区的,因此它是按照一个区的维度来分的。

 

但是有些卖家挣的钱多,有的卖家挣的钱少,虽然同属于一个菜市场,但是挣钱少的跟挣钱多的一样抽成,他们也不乐意,因此又按照卖家的ID进行抽查。

 

最终根据业务的形态以及我们的市场调查结果,采用以商户为基准,按照菜品的二级分类来抽成的一种盈利模式。

 

2. 数据库系统架构设计如下:

 

卖家抽点配置信息表:

复制代码
CREATE TABLE `order_percentage_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ''主键,自动增加ID'',
  `region_id` bigint(20) NOT NULL COMMENT ''区域id'',
  `seller_id` bigint(20) DEFAULT NULL COMMENT ''卖家id'',
  `category_id` bigint(20) DEFAULT NULL COMMENT ''商品二级分类的ID'',
  `percentage` decimal(12,2) DEFAULT NULL COMMENT ''平台抽点率 '',
  `status` tinyint(1) DEFAULT NULL COMMENT ''状态: 0禁用 ,1启用'',
  `create_time` datetime DEFAULT NULL COMMENT ''创建时间 '',
  `update_time` datetime DEFAULT NULL COMMENT ''修改时间'',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=144 DEFAULT CHARSET=utf8 COMMENT=''订单抽点配置信息表'';
复制代码

 

说明:按照卖家的所属二级分类进行抽点,不同的抽点率也是不一样的。

 

2. 卖家抽点明细表。(系统需要精确的记录,那个菜品进行了抽点,抽点多少,原来的多少钱,抽点后多少钱,需要明明白白的,让卖家清清楚楚的有一笔账)

复制代码
CREATE TABLE `order_percentage` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT ''自动增加ID'',
  `order_item_id` bigint(20) DEFAULT NULL COMMENT ''订单item的ID'',
  `order_number` varchar(64) DEFAULT NULL COMMENT ''所属订单号'',
  `seller_id` bigint(20) DEFAULT NULL COMMENT ''卖家id'',
  `order_total_amount` decimal(12,2) DEFAULT NULL COMMENT ''订单金额'',
  `order_percentage` decimal(12,2) DEFAULT NULL COMMENT ''抽点比率'',
  `order_percentage_amount` decimal(12,2) DEFAULT NULL COMMENT ''抽点金额'',
  `order_reality_money` decimal(12,2) DEFAULT NULL COMMENT ''订单最终金额即用户最终收到的金额'',
  `create_time` datetime DEFAULT NULL COMMENT ''所属时间'',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=47226 DEFAULT CHARSET=utf8 COMMENT=''抽点信息总表'';
复制代码

补充说明:1. 由于的是对某一个明细进行抽点。所以需要卖家ID,订单明细ID以及抽点比率等,最终算出抽点金额。

 

3. 何时进行抽点呢、

    回答:每天早上6点30进行昨天的抽点,形成自己的账单。

   相关的核心代码如下:采用spring task做定时器

 

复制代码
/**
     * 计算订单抽成 每天早上6:30点执行
     */
    @Scheduled(cron = "0 30 6 * * ?")
    protected void makeOrderPercentage() {
        try {
            logger.info("TasksQuartz.makeOrderPercentage.start");
            // 计算订单抽成,并生成抽成数据
            orderPercentageService.batchSaveOrderPercentage();
            logger.info("TasksQuartz.makeOrderPercentage.end");
        } catch (Exception ex) {
            logger.error("TasksQuartz.makeOrderPercentage.exception", ex);
        }
    }
复制代码

 

   业务核心代码:

 

复制代码
@Service
public class OrderPercentageserviceImpl implements OrderPercentageService {

    private static final Logger logger = LoggerFactory.getLogger(OrderPercentageserviceImpl.class);

    @Autowired
    private OrderItemDao orderItemDao;

    @Autowired
    private OrderPercentageConfigDao orderPercentageConfigDao;

    @Autowired
    private OrderPercentageDao orderPercentageDao;

    /**
     * 批量新增数据
     */
    @Override
    public void batchSaveOrderPercentage() {

        // 1.获取昨天12:00到今天早上6:00的所有的订单明细
        Map<String, Object> dateMap = DateUtil.getTradeTime(0);

        List<OrderItem> orderItems = orderItemDao.getOrderItemsByDate(dateMap);

        List<OrderPercentage> list = new ArrayList<OrderPercentage>();

        if (CollectionUtils.isEmpty(orderItems)) {
            return;
        }

        for (OrderItem item : orderItems) {
            try {
                Long formatId = item.getFormatId();
                Long sellerId = item.getSellerId();

                OrderPercentageConfig config = orderPercentageConfigDao.getOrderPercentageConfig(formatId, sellerId);

                if (config == null) {
                    logger.error("batchSaveOrderPercentage.config.isEmpty:" + "formatId:" + formatId + " sellerId:"
                            + sellerId);
                    continue;
                }

                OrderPercentage orderPercentage = new OrderPercentage();
                // 订单总额
                BigDecimal orderTotalAmount = item.getGoodsAmount();

                // 抽点比率
                BigDecimal percentage = config.getPercentage();

                // 抽点金额
                BigDecimal orderPercentageAmount = orderTotalAmount.multiply(percentage)
                        .multiply(new BigDecimal("0.01")).setScale(3, BigDecimal.ROUND_UP);

                // 卖家最终所得
                BigDecimal orderRealityMoney = orderTotalAmount.subtract(orderPercentageAmount).setScale(3,
                        BigDecimal.ROUND_UP);

                orderPercentage.setCreateTime(new Date());
                orderPercentage.setOrderItemId(item.getId());
                orderPercentage.setOrderNumber(item.getOrderNumber());
                orderPercentage.setOrderTotalAmount(orderTotalAmount);
                orderPercentage.setOrderPercentage(percentage);
                orderPercentage.setOrderPercentageAmount(orderPercentageAmount);
                orderPercentage.setOrderRealityMoney(orderRealityMoney);
                orderPercentage.setSellerId(sellerId);
                list.add(orderPercentage);
            } catch (Exception ex) {
                logger.error("batchSaveOrderPercentage.exception", ex);
            }
        }

        try {
            orderPercentageDao.batchSaveOrderPercentage(list);
        } catch (Exception ex) {
            logger.error("batchSaveOrderPercentage", ex);
        }
    }

}
复制代码

 

相关后台运营截图如下;

 

 

 

 

 

 

 

转载自-- https://www.cnblogs.com/jurendage/p/9059304.html

14、生鲜电商平台-搜索模块的设计与架构

14、生鲜电商平台-搜索模块的设计与架构

说明:搜索模块针对的是买家用户,在找菜品找的很费劲下的一种查询方面。目前也是快速的检索商品。

           对于移动端的APP买家用户而言,要求的速度在3秒内完成。支持模糊查询,由于业务实战表面,整个搜索频率不到18%-25%之间

          同时业务也不算很大,所以并没采用java全文检索技术.(lucene等)。这里采用的就是基本的模糊查询。

 

1. 搜索维度的是思考。

             1.1  买家搜索的内容很有可能是针对菜品的本身属性而言,所以涉及到的内容有商品名称,商品别名,商品标签,商品描述,规格的名称,加工方式等。

             1.2  我们知道模糊搜索会导致索引失效,同时整个查询性能也是有影响的。

             1.3 业务形态显示有些热点的词语与内容可以做JVM缓存以提高整个单品的购买率。比如土豆现在分析出很多人要,如果我们可以跟某个商家谈好,一天

需要10w斤土豆的量进行供应,那么整个页面会出现这个关键字的默认显示。这个是后端灵活配置的。

 

2. 对于买家搜索的关键字,我们需要数据库进行记录,这样可以从系统级别算出买家会需要什么,可以进行针对性的营销.

 

相关数据库表的设计如下:

复制代码
CREATE TABLE `buyer_search` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT ''自动增加ID'',
  `buyer_id` bigint(20) DEFAULT NULL COMMENT ''买家ID'',
  `words` bigint(20) DEFAULT NULL COMMENT ''卖家ID'',
  `create_time` datetime DEFAULT NULL COMMENT ''创建时间'',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=''买家搜索记录表'';
复制代码

补充说明:目的其实是很明确的,就是记录买家搜索的关键字以便分析与研究用。为了更好的体验用户。

 

相关业务代码如下:

复制代码
/**
     * APP全文搜索 新
     * @param request
     * @param response
     * @param keyword
     * @return
     */
    @RequestMapping(value = "/search/new", method = { RequestMethod.GET, RequestMethod.POST })
    public JsonResult newSearchGoods(HttpServletRequest request, HttpServletResponse response,Long userId,Long regionId,String keyword){
        try{
            logger.info("SearchController.search.keyword:搜索内容:" + keyword);
            //搜索结果按商家显示
            //List<SellerVo> list = sellerService.searchSeller(regionId,keyword);
            //搜索结果按商品显示
            List<NewSearchVo> list = goodsService.newSearchGoods(userId,regionId,keyword);
            return new JsonResult(JsonResultCode.SUCCESS, "查询信息成功", list);
        }catch(Exception ex){
            logger.error("[SearchController][newSearchGoods] exception :",ex);
            return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试","");
        }
    }
复制代码

 

VO对象如下:

复制代码
/**
 * 搜索显示类(APP全局搜索)
 */
public class NewSearchVo implements Serializable{

    private static final long serialVersionUID = 1L;
        
    /**
     * 来源于users的ID
     */
    private Long sellerId;

    /**
     * 店铺名称
     */
    private String sellerName;
    
    /**
     * 店铺别名,可以理解为简称
     */
    private String sellerAlias;
    
    /**
     * 店铺logo
     */
    private String sellerLogo;
    
    /**
     * 店铺评级,默认为0
     */
    private int sellerRank;
    
    /**
     * 店铺评分
     */
    private Double sellerGrade;
    
    /**
     * 搜索后商品列表
     */
    private List<GoodsVo> searchItemList;

    public Long getSellerId() {
        return sellerId;
    }

    public void setSellerId(Long sellerId) {
        this.sellerId = sellerId;
    }

    public String getSellerName() {
        return sellerName;
    }

    public void setSellerName(String sellerName) {
        this.sellerName = sellerName;
    }

    public String getSellerAlias() {
        return sellerAlias;
    }

    public void setSellerAlias(String sellerAlias) {
        this.sellerAlias = sellerAlias;
    }

    public String getSellerLogo() {
        return sellerLogo;
    }

    public void setSellerLogo(String sellerLogo) {
        this.sellerLogo = sellerLogo;
    }

    public int getSellerRank() {
        return sellerRank;
    }

    public void setSellerRank(int sellerRank) {
        this.sellerRank = sellerRank;
    }

    public Double getSellerGrade() {
        return sellerGrade;
    }

    public void setSellerGrade(Double sellerGrade) {
        this.sellerGrade = sellerGrade;
    }

    public List<GoodsVo> getSearchItemList() {
        return searchItemList;
    }

    public void setSearchItemList(List<GoodsVo> searchItemList) {
        this.searchItemList = searchItemList;
    }
}
复制代码

 

3, 数据查询性能暂时的基本满足要求,也贴出来给大家一起参考,目的是共同学习与思考.

 

复制代码
<!-- 全局搜索商品  新-->
    <select id="newSearchGoods" resultMap="newSearchResult">
        <include refid="newSearchSelect" />
        <include refid="newSearchFrom" />
        <include refid="searchWhere" />
        <if test=" keyword != null">
            and (g.goods_name like concat(''%'',#{keyword},''%'')
            or g.goods_as like concat(''%'',#{keyword},''%'')
            or g.goods_label like concat(''%'',#{keyword},''%'')
            or g.goods_brand like concat(''%'',#{keyword},''%'')
            or g.goods_desc like concat(''%'',#{keyword},''%'') 
            or gf.format_name like concat(''%'',#{keyword},''%'') 
            or pm.method_name like concat(''%'',#{keyword},''%'') 
            or s.seller_name like concat(''%'',#{keyword},''%'') 
            or exists(select 1 from category where category_id = g.category_id and category_name like concat(''%'',#{keyword},''%'') )
            ) 
        </if>
        <include refid="searchOrderBy" />
    </select>
复制代码

 

总结,由于搜索这块所涉及的业务相对而言比较少,功能也比较单一,含金量不是很高,所以互相学习。

 

对于扩展方案,如果这块的业务发现很大,可以采用中文分词记录,进行数据的挖掘,已经冷热点数据的一个分离等等,这个后期大家有需要的话,我们再研究。

 

相关业务运营截图如下:

 

 

 

转载自-- https://www.cnblogs.com/jurendage/p/9062649.html

15、生鲜电商平台-售后模块的设计与架构

15、生鲜电商平台-售后模块的设计与架构

说明:任何一个的电商平台都有售后服务系统,那么对于我们这个生鲜的电商平台,售后系统需要思考以下几个维度。

 

          1. 买家的需求维度

             说明:买家在平台上没找到自己想要的东西,我们需要提供给他一个入口,告诉我们他有这个需求,我们进行改进。系统需要有记录这种情况,同时也有回复客户的情况。

          2. 投诉入口

              说明:有客户性子比较急,他有问题,就会马上打电话给客服,客服需要解答与回答,维护客户关系。对于系统而言,需要记录这种情况,然后分析问题与解决问题。

          3. IM聊天入口

             说明:客户有时候也不想写信息,也不想打电话,能否有一个时刻的IM聊天记录呢?对于系统而言需要记录这种信息,我们目前系统没处理,采用的是微信,以及销售人员的反馈机制。

          4. 退货问题

              说明:售后系统中,退货问题是最繁琐的,买家存在以下两种情况。

                      4.1 买家要钱不要货。顾名思义,有些买家就是不要货了,他需要我们退钱给他,这个配送端有一个一件退货功能,钱退到买家的余额里面,下次可以继续购买。

                      4.2  买家要货不要钱,顾名思义,有些买家的确需要这个货物,对于我们退钱给他,他是不接受的,因为他真的需要这种东西,你让他再去买,客户体验非常差,可能 就没有下次购物了。对于这种情况,我们用时间轴来继续整个过程。(说明,由于这个系统设计到生鲜电商方面,其他的电商方面可能会不一样。)

 

相关数据库的设计与架构如下:

 1. 买家平台建议信息表

复制代码
CREATE TABLE `suggestion` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT ''自动增加ID'',
  `suggestion_content` varchar(1024) DEFAULT NULL COMMENT ''建议内容'',
  `suggestion_imgs` varchar(255) DEFAULT NULL COMMENT ''多张图片'',
  `user_id` bigint(20) DEFAULT NULL COMMENT ''所属用户ID'',
  `create_time` datetime DEFAULT NULL COMMENT ''创建时间'',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8 COMMENT=''用户对平台的建议'';
复制代码

说明: 平台建议表,是买家对平台的建议以及自己的需求的一个入口,可以是图片与内容两点。

          比如说:他说我们送的菜有问题,很多烂的,那么他是需要拍图片证明的。

 

2. 平台回复信息表

复制代码
CREATE TABLE `suggestion_reply` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT ''自动增加ID'',
  `suggestion_id` bigint(20) DEFAULT NULL COMMENT ''客户的建议ID'',
  `content` varchar(512) DEFAULT NULL COMMENT ''回复的内容'',
  `create_time` datetime DEFAULT NULL COMMENT ''创建时间'',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8 COMMENT=''客户建议回复信息表'';
复制代码

说明:作为一个平台,平台需要回复客户的信息,买家也需要看到,当然这边系统是不区分是买家还是卖家的,我们都是可以数据的处理的。

 

3. 售后系统时间轴的设计

说明:其实我们系统需要知道整个售后的过程的,比如买家什么时候发起的不要钱,要货,然后师傅是什么时候知道这个消息的,如何进行售后的,他们会遇到什么问题,

当然这里面有很多的问题,系统可以做的事情其实是很少的,而我们需要做的是更多的事情。

 

复制代码
CREATE TABLE `order_timeline` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT ''自动增加ID'',
  `item_id` bigint(20) DEFAULT NULL COMMENT ''订单项ID'',
  `remarks` varchar(256) DEFAULT NULL COMMENT ''备注'',
  `create_time` datetime DEFAULT NULL COMMENT ''创建时间'',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1980 DEFAULT CHARSET=utf8 COMMENT=''售后模块,退换货时间轴,针对的是某一个订单项'';
复制代码

 

相关时间轴运营截图如下:

 

 

 

 整个业务不算复杂,需要的一种思路与解决思路的方案:

关于补货流程:

复制代码
补货需求
业务需求:
    当卖家主动点击缺货,则配送师傅看到这个异常订单项,然后他有两种选择,
第一种补货(有货,他也想补或者客户说要货不要钱)
第二种不补货(无货可补,他不想补或者客户说退钱等等)
第一种补货业务:
1.    当配送师傅点击已补货,则把这个订单项对应的金额从买家中直接扣除,前提是线上付款,如果这个订单是线下付款,则不用处理扣款逻辑,直接修改状态即可。同时记录时间轴日志。
第二种不补货业务:
2.    当师傅点击不补货,则这个订单项不做任何扣款逻辑,不管线下还是线上,直接修改状态即可,同时记录时间轴日志。
补充说明:补货与不补货属于互斥操作,即已补货后不允许再出现不补货,不补货后不再允许出现补货。按照规则来处理。
复制代码

 

 相关业务核心代码如下:

复制代码
/**
 * 订单项退货*/
@RestController
@RequestMapping("/delivery")
public class OrderReturnController extends BaseController {

    private static final Logger logger = LoggerFactory.getLogger(OrderReturnController.class);

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private OrderReturnService orderReturnService;

    /**
     * 订单项退货
     * 
     */
    @RequestMapping(value = "/order/return/item", method = { RequestMethod.GET, RequestMethod.POST })
    public JsonResult orderReturnItem(HttpServletRequest request, HttpServletResponse response,
            @Param("itemId") Long itemId, @Param("deliveryId") Long deliveryId, @Param("status") int status) {
        try {
            if (itemId == null) {
                return new JsonResult(JsonResultCode.FAILURE, "item参数有误", "");
            }

            OrderItem orderItem = orderItemService.getOrderItemByItemId(itemId);

            if (orderItem == null) {
                return new JsonResult(JsonResultCode.FAILURE, "无此订单项", "");
            }
            
            String returnMsg = "";
            if(status == BuyerStatus.THREE){
                returnMsg = TimelineTemplate.return_MSG;
            }if(status == BuyerStatus.FOUR){
                returnMsg = TimelineTemplate.BACK_MSG;
            }if(status == BuyerStatus.ZERO){
                returnMsg = TimelineTemplate.OFF_MSG;
            }
            orderItemService.updateOrderItemStatus(itemId, status, deliveryId, returnMsg);
            return new JsonResult(JsonResultCode.SUCCESS, "操作成功", "");
        } catch (Exception ex) {
            logger.error("[OrderReturnController][orderReturnItem] exception :", ex);
            return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试", "");
        }
    }

    /**
     * 退还列表
     */
    @RequestMapping(value = "/order/return/list", method = { RequestMethod.GET, RequestMethod.POST })
    public JsonResult orderReturnList(HttpServletRequest request, HttpServletResponse response,@Param("deliveryId") Long deliveryId, @Param("status") int status) {
        try 
        {
            // 组装成为最终的列表结果
            List<OrderReturnVo> listResult = new ArrayList<OrderReturnVo>();

            List<OrderGoodsVo> goodsList = orderReturnService.getReturnOrderGoodsList(deliveryId,status);

            if (CollectionUtils.isEmpty(goodsList)) {
                return new JsonResult(JsonResultCode.SUCCESS, "查询完成", listResult);
            }

            // 临时参数,判断时间
            Map<String, List<OrderReturnEntity>> paramTimeMap = new HashMap<String, List<OrderReturnEntity>>();

            // 过滤卖家
            Map<String, List<OrderGoodsVo>> paramSellerMap = new HashMap<String, List<OrderGoodsVo>>();

            for (OrderGoodsVo vo : goodsList) {
                String bestTime = DateUtil.dateToString(vo.getBestTime(), "yyyy-MM-dd");

                // 时间相同
                if (paramTimeMap.get(bestTime) != null) {
                    List<OrderReturnEntity> mapOrderReturnEntity = paramTimeMap.get(bestTime);

                    // 组装时间
                    OrderReturnVo resultVo = new OrderReturnVo();
                    resultVo.setBestTime(bestTime);

                    // 判断是否是同一个卖家的
                    if (paramSellerMap.get(vo.getSellerName()) != null) 
                    {
                        OrderReturnEntity entity = new OrderReturnEntity();
                        List<OrderGoodsVo> listVo = paramSellerMap.get(vo.getSellerName());
                        listVo.add(vo);
                        entity.setListOrderGoodsVo(listVo);
                        resultVo.setListOrderReturnEntity(mapOrderReturnEntity);
                    }else
                    {
                        //不同买家
                        OrderReturnEntity entity = new OrderReturnEntity();
                        entity.setSellerName(vo.getSellerName());
                        
                        List<OrderGoodsVo> listVo =new ArrayList<OrderGoodsVo>();
                        listVo.add(vo);
                        entity.setListOrderGoodsVo(listVo);
                        mapOrderReturnEntity.add(entity);
                        
                        paramSellerMap.put(vo.getSellerName(), listVo);
                    }
                } else {
                    // 组装时间
                    OrderReturnVo resultVo = new OrderReturnVo();
                    resultVo.setBestTime(bestTime);

                    OrderReturnEntity entity = new OrderReturnEntity();
                    entity.setSellerName(vo.getSellerName());

                    List<OrderGoodsVo> paramOrderGoodsVo = new ArrayList<OrderGoodsVo>();
                    paramOrderGoodsVo.add(vo);
                    entity.setListOrderGoodsVo(paramOrderGoodsVo);

                    List<OrderReturnEntity> listOrderReturnEntity = new ArrayList<OrderReturnEntity>();
                    listOrderReturnEntity.add(entity);

                    resultVo.setListOrderReturnEntity(listOrderReturnEntity);

                    listResult.add(resultVo);

                    paramSellerMap.put(vo.getSellerName(), paramOrderGoodsVo);

                    paramTimeMap.put(bestTime, listOrderReturnEntity);
                }
            }
            return new JsonResult(JsonResultCode.SUCCESS, "查询信息成功", listResult);
        } catch (Exception ex) {
            logger.error("[OrderReturnController][orderReturnItem] exception :", ex);
            return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试", "");
        }
    }
复制代码

 

APP运营截图相对而言比较简单,我这边就不贴出来了。

 

转载自-- https://www.cnblogs.com/jurendage/p/9066307.html

关于36、生鲜电商平台-积分,优惠券,会员折扣,签到、预售、拼团、砍价、秒杀及抽奖等促销模块架构设计的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于10、生鲜电商平台-财务系统模块的设计与架构、13、生鲜电商平台-订单抽成模块的设计与架构、14、生鲜电商平台-搜索模块的设计与架构、15、生鲜电商平台-售后模块的设计与架构等相关知识的信息别忘了在本站进行查找喔。

本文标签: