跳到主要内容

拼团

一、功能说明

1、拼团促销活动属于商家店铺可直接发布的促销活动。

2、同一个商家在同一时间段内允许创建多个拼团促销活动。

3、同一个商品在同一时间段内只允许参与一个拼团促销活动。

4、正在进行的拼团活动,商家也可以进行修改和删除,不影响已经形成的订单(包括未付款的)。

5、商家修改或删除正在进行的拼团活动对拼团商品的影响:

删除操作:刷新当前活动商品页,会提示该活动商品不存在或已失效 修改活动日期操作:刷新当前活动商品页,会提示该活动商品不存在或已失效 修改活动商品价格:刷新当前活动商品页显示为修改后的价格

6、商家修改或删除正在进行的拼团活动对创建订单的影响:

如果客户已经在拼团的结算页,此时商家 修改了商品拼团价格,然后客户又在结算页直接创建订单,此时价格按修改后的商品价格计算

如果客户已经在拼团的结算页,此时商家删除了此商品(取消此商品参加),然后客户又在结算页直接创建订单 ,则会提示:“此活动已经取消拼团活动,不能发起拼团”

7、商家账号不能参与自己创建的拼团活动。

8、同一个用户可以对同一个拼团商品发起多次拼团。

9、平台管理端可以关闭正在进行的商家拼团促销活动。

关闭活动只是关闭入口,并不是关闭已经创建的订单

10、拼团活动自动结束时或者手动关闭时会判定虚拟成团情况:

  • 未付款订单变成已取消;

  • 已付款非虚拟变成已取消并生成退款单支持原路退回,则原路退回,不支持则管理员手动退款;

  • 已付款并且虚拟成团,变成已成团待发货状态

二、数据库设计

1、表结构设计

  1. 拼团促销活动表—es_pintuan

    字段名类型与长度说明
    promotion_idbigint(20)主键ID
    promotion_namevarchar(255)活动名称
    promotion_titlevarchar(50)活动标题
    start_timebigint(20)活动开始时间(存放的是以秒为单位的时间戳)
    end_timebigint(20)活动结束时间(存放的是以秒为单位的时间戳)
    required_numint(10)成团人数
    limit_numint(10)限购数量(如果为0则代表不限购)
    enable_mockersmallint(1)是否开启了虚拟成团 0:否,1:是
    create_timebigint(20)活动创建时间
    statusvarchar(50)活动状态 WAIT:等待开始,UNDERWAY:进行中,END:已结束
    option_statusvarchar(50)API请求操作状态 CAN_OPEN:可以开启,CAN_CLOSE:可以关闭,NOTHING:没有什么可以操作
    seller_namevarchar(255)活动所属商家名称
    seller_idbigint(20)活动所属商家ID(关联es_shop表)
  1. 拼团促销活动商品表—es_pintuan_goods

    字段名类型与长度说明
    idbigint(20)主键ID
    sku_idbigint(20)商品SKU(关联es_goods_sku表)
    goods_namevarchar(255)商品名称
    origin_pricedecimal(20,2)商品原价格
    sales_pricedecimal(20,2)商品拼团价格
    snvarchar(50)商品编号
    sold_quantityint(10)商品已售数量
    locked_quantityint(10)商品待发货数量
    pintuan_idbigint(20)拼团活动ID(关联es_pintuan表)
    goods_idbigint(20)商品ID(关联es_goods表)
    specslongtext商品规格信息
    seller_idbigint(20)商品所属商家ID(关联es_shop表)
    seller_namevarchar(255)商品所属商家名称
    thumbnailvarchar(255)商品缩略图
  1. 拼团活动订单表—es_pintuan_order

    字段名类型与长度说明
    order_idbigint(20)主键ID
    pintuan_idbigint(20)拼团活动ID(关联es_pintuan表)
    end_timebigint(20)活动结束时间(存放的是以秒为单位的时间戳)
    sku_idbigint(20)商品SKU(关联es_goods_sku表)
    required_numint(10)成团人数
    offered_numint(10)已参团人数
    offered_personslongtext参团人信息
    order_statusvarchar(50)订单状态 new_order:新订单,wait:待成团,pay_off:已付款,formed:已成团,cancel:已取消
    goods_idbigint(20)商品ID(关联es_goods表)
    thumbnailvarchar(255)商品缩略图
    goods_namevarchar(255)商品名称
  1. 拼团活动子订单表—es_pintuan_child_order

    字段名类型与长度说明
    child_order_idbigint(20)主键ID
    order_snvarchar(50)订单编号(关联es_order表)
    member_idbigint(20)会员ID(关联es_member表)
    sku_idbigint(20)商品SKU(关联es_goods_sku表)
    pintuan_idbigint(20)拼团活动ID(关联es_pintuan表)
    order_statusvarchar(50)订单状态 new_order:新订单,wait:待成团,pay_off:已付款,formed:已成团,cancel:已取消
    order_idbigint(20)主订单ID(关联es_pintuan_order表)
    member_namevarchar(50)会员名称
    origin_pricedecimal(20,2)订单原价
    sales_pricedecimal(20,2)订单拼团价格

2、表关联说明

  • 拼团活动相关表与表关联图

    image-20201104113036215

  • 拼团相关表与表之间的关联字段对照

    拼团促销活动表(es_pintuan)拼团活动商品表(es_pintuan_goods)
    promotion_idpintuan_id
    拼团促销活动表(es_pintuan)拼团活动订单表(es_pintuan_order)
    promotion_idpintuan_id
    拼团促销活动表(es_pintuan)拼团活动子订单表(es_pintuan_child_order)
    promotion_idpintuan_id
    拼团活动订单表(es_pintuan_order)拼团活动子订单表(es_pintuan_child_order)
    order_idorder_id
    拼团活动子订单表(es_pintuan_child_order)订单表(es_order)
    order_snsn

三、缓存设计

拼团促销活动脚本引擎

  • 脚本引擎缓存结构:

    拼团促销活动的促销脚本引擎缓存结构只有一种:SKU级别的缓存结构。

  • 脚本引擎生成和删除时机:

    • 生成:拼团活动开始时生成。

      如果活动开始了,商家又新增了参与活动的商品,此时也会更新脚本信息。

    • 删除:拼团活动结束时删除。

关于促销脚本引擎缓存结构可参考《促销活动脚本引擎生成架构》这篇文档。

四、索引设计

参与拼团活动的商品会创建单独的索引结构

拼团商品索引Mapping信息如下:

image-20201104143824661

也就是说,拼团商品在生成索引的时候,只会将如上的商品信息放入到ES中。

五、代码设计

1、接口调用流程图

  • 商家发布促销活动

    image-20201104145758484

  • 添加参与促销活动的商品

    image-20201104151228527

  • 平台管理端对拼团活动的操作

    image-20201104154636060

2、相关代码展示

  • 商家发布促销活动

    @RestController
    @RequestMapping("/seller/promotion/pintuan")
    @Api(description = "拼团")
    @Validated
    public class PintuanSellerController {

    @ApiOperation(value = "添加活动")
    @PostMapping
    public Pintuan add(@Valid Pintuan pintuan) {
    return this.pintuanManager.add(pintuan);
    }
    }
    @Service
    public class PintuanManagerImpl implements PintuanManager {

    @Override
    @Transactional(value = "tradeTransactionManager",
    propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Pintuan add(Pintuan pintuan) {
    //检测开始时间和结束时间
    PromotionValid.paramValid(pintuan.getStartTime(), pintuan.getEndTime(),
    1, null);
    this.verifyParam(pintuan.getStartTime(), pintuan.getEndTime());
    pintuan.setStatus(PromotionStatusEnum.WAIT.name());
    pintuan.setSellerName(UserContext.getSeller().getSellerName());
    pintuan.setCreateTime(DateUtil.getDateline());
    pintuan.setSellerId(UserContext.getSeller().getSellerId());
    //可操作状态为nothing,代表活动不可以执行任何操作
    pintuan.setOptionStatus(PintuanOptionEnum.NOTHING.name());
    this.pintuanMapper.insert(pintuan);

    //创建活动 启用延时任务
    PintuanChangeMsg pintuanChangeMsg = new PintuanChangeMsg();
    pintuanChangeMsg.setPintuanId(pintuan.getPromotionId());
    pintuanChangeMsg.setOptionType(1);
    timeTrigger.add(TimeExecute.PINTUAN_EXECUTER, pintuanChangeMsg,
    pintuan.getStartTime(), TRIGGER_PREFIX +
    pintuan.getPromotionId());
    return pintuan;
    }
    }
  • 商家选择商品参与团购活动

    @RestController
    @RequestMapping("/seller/promotion/pintuan")
    @Api(description = "拼团")
    @Validated
    public class PintuanSellerController {

    @ApiOperation(value = "修改活动参与的商品", response = PintuanGoodsDO.class)
    @PostMapping("/goods/{id}")
    @ApiImplicitParams({
    @ApiImplicitParam(name = "id", value = "活动ID",
    required = true, dataType = "int", paramType = "path")
    })
    public void savePromotionGoods(@RequestBody @Valid List<PintuanGoodsDO>
    pintuanGoodsDOS, @ApiIgnore @PathVariable Long id) {
    this.pintuanGoodsManager.save(id, pintuanGoodsDOS);
    }
    }
    @Service
    public class PintuanGoodsManagerImpl implements PintuanGoodsManager {

    @Override
    @Transactional(value = "tradeTransactionManager",
    propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void save(Long pintuanId, List<PintuanGoodsDO> pintuanGoodsList) {
    //获取拼团促销活动信息详情
    Pintuan pintuan = pintuanManager.getModel(pintuanId);

    //获取本次操作之前参与拼团活动的商品信息集合
    List<PintuanGoodsDO> oldGoodsList = this.getPintuanGoodsList(pintuanId);


    Long[] skuIds = new Long[pintuanGoodsList.size()];

    for (int i = 0; i < pintuanGoodsList.size(); i++) {
    skuIds[i] = pintuanGoodsList.get(i).getSkuId();
    }
    //拼团促销活动
    List<PromotionAbnormalGoods> promotionAbnormalGoods
    = this.checkPromotion(skuIds, pintuan.getStartTime(), pintuan.getEndTime(),
    pintuan.getPromotionId());

    StringBuffer stringBuffer = new StringBuffer();
    if (promotionAbnormalGoods.size() > 0) {
    promotionAbnormalGoods.forEach(pGoods -> {
    stringBuffer.append("商品【" + pGoods.getGoodsName()
    + "】参与【" + PromotionTypeEnum.valueOf(
    pGoods.getPromotionType()).getPromotionName()
    + "】活动,");

    stringBuffer.append("时间冲突【" + DateUtil.toString(
    pGoods.getStartTime(), "yyyy-MM-dd HH:mm:ss")
    + "~" + DateUtil.toString(pGoods.getEndTime(),
    "yyyy-MM-dd HH:mm:ss")
    + "】;");
    });
    throw new ServiceException(PintuanErrorCode.E5015.code(),
    stringBuffer.toString());
    }

    this.pintuanGoodsMapper.delete(new QueryWrapper<PintuanGoodsDO>()
    .eq("pintuan_id", pintuan.getPromotionId()));

    pintuanGoodsList.forEach(pintuanGoodsDO -> {

    pintuanGoodsDO.setPintuanId(pintuan.getPromotionId());
    GoodsSkuVO skuVo = goodsClient.getSkuFromCache(pintuanGoodsDO.getSkuId());
    //验证拼团价格和原始价格
    if (pintuanGoodsDO.getSalesPrice() > skuVo.getPrice()) {
    throw new ServiceException(PintuanErrorCode.E5015.code(),
    skuVo.getGoodsName()
    + ",此商品的拼团价格不能大于商品销售价格");
    }
    pintuanGoodsDO.setSellerId(skuVo.getSellerId());
    pintuanGoodsDO.setSellerName(skuVo.getSellerName());
    pintuanGoodsDO.setThumbnail(skuVo.getThumbnail());
    pintuanGoodsDO.setSpecs(StringUtil.notEmpty(skuVo.getSpecs()) ?
    skuVo.getSpecs() :
    JsonUtil.objectToJson(skuVo.getSpecList()));
    pintuanGoodsDO.setOriginPrice(skuVo.getPrice());
    pintuanGoodsDO.setGoodsId(skuVo.getGoodsId());
    pintuanGoodsDO.setGoodsName(skuVo.getGoodsName());
    pintuanGoodsDO.setLockedQuantity(0);
    pintuanGoodsDO.setSoldQuantity(0);
    pintuanGoodsDO.setSn(skuVo.getSn());
    add(pintuanGoodsDO);

    });

    //如果拼团活动是进行中的状态,则同步商品的索引和脚本信息
    if (pintuan.getStatus().equals(PromotionStatusEnum.UNDERWAY.name())) {
    //同步es
    pinTuanSearchManager.syncIndexByPinTuanId(pintuanId);

    //先将本次操作之前存放在缓存中参与拼团活动的商品脚本信息删除
    this.pintuanScriptManager.deleteCacheScript(pintuanId, oldGoodsList);
    //然后为本次操作添加的活动商品创建脚本信息
    this.pintuanScriptManager.createCacheScript(pintuanId, pintuanGoodsList);
    }
    }
    }
  • 平台管理端停止和开启拼团活动

    @RestController
    @RequestMapping("/admin/promotion/pintuan")
    @Api(description = "拼团API")
    @Validated
    public class PintuanManagerController {

    @PutMapping(value = "/{id}/close")
    @ApiOperation(value = "关闭拼团")
    @ApiImplicitParams({
    @ApiImplicitParam(name = "id", value = "要关闭的拼团入库主键", required = true,
    dataType = "int", paramType = "path")
    })
    public void close(@PathVariable Long id) {
    this.pintuanManager.manualClosePromotion(id);

    }

    @PutMapping(value = "/{id}/open")
    @ApiOperation(value = "开启拼团")
    @ApiImplicitParams({
    @ApiImplicitParam(name = "id", value = "要关闭的拼团入库主键", required = true,
    dataType = "int", paramType = "path")
    })
    public void open(@PathVariable Long id) {

    this.pintuanManager.manualOpenPromotion(id);

    }
    }
    @Service
    public class PintuanManagerImpl implements PintuanManager {

    /**
    * 手动停止一个活动
    *
    * @param promotionId
    */
    @Override
    public void manualClosePromotion(Long promotionId) {
    if (check(promotionId, 0)) {
    this.closePromotion(promotionId);
    } else {
    throw new ServiceException(PintuanErrorCode.E5012.code(),
    PintuanErrorCode.E5012.describe());
    }
    }

    /**
    * 手动开始一个活动
    *
    * @param promotionId
    */
    @Override
    public void manualOpenPromotion(Long promotionId) {
    if (check(promotionId, 1)) {
    this.openPromotion(promotionId);
    } else {
    throw new ServiceException(PintuanErrorCode.E5012.code(),
    PintuanErrorCode.E5012.describe());
    }
    }

    /**
    * 开始一个活动
    *
    * @param promotionId
    */
    @Override
    @Transactional(value = "tradeTransactionManager", propagation =
    Propagation.REQUIRED, rollbackFor = Exception.class)
    public void openPromotion(Long promotionId) {

    //获取拼团活动详情
    Pintuan pintuan = this.getModel(promotionId);
    //获取参与拼团活动的商品信息集合
    List<PintuanGoodsDO> goodsList =
    this.pintuanGoodsManager.getPintuanGoodsList(promotionId);

    UpdateWrapper updateWrapper = new UpdateWrapper<>()
    .eq("promotion_id", promotionId);
    //如果还在活动时间内
    //修改状态为进行中,活动可操作状态变成可以关闭
    if (pintuan.getEndTime() > DateUtil.getDateline()) {
    updateWrapper.set("status", PromotionStatusEnum.UNDERWAY.name());
    updateWrapper.set("option_status", PintuanOptionEnum.CAN_CLOSE.name());

    //生成拼团商品索引
    pintuanGoodsManager.addIndex(promotionId);
    //生成拼团商品脚本信息
    pintuanScriptManager.createCacheScript(promotionId, goodsList);
    } else {
    //活动时间范围外,修改状态为已结束,活动可操作状态变成nothing
    updateWrapper.set("status", PromotionStatusEnum.END.name());
    updateWrapper.set("option_status", PintuanOptionEnum.NOTHING.name());
    //删除拼团商品脚本信息
    pintuanScriptManager.deleteCacheScript(promotionId, goodsList);
    }

    this.pintuanMapper.update(null, updateWrapper);

    }

    /**
    * 停止一个活动
    *
    * @param promotionId
    */
    @Override
    @Transactional(value = "tradeTransactionManager", propagation =
    Propagation.REQUIRED, rollbackFor = Exception.class)
    public void closePromotion(Long promotionId) {

    //获取拼团活动详情
    Pintuan pintuan = this.getModel(promotionId);
    //获取参与拼团活动的商品信息集合
    List<PintuanGoodsDO> goodsList =
    this.pintuanGoodsManager.getPintuanGoodsList(promotionId);

    UpdateWrapper updateWrapper = new UpdateWrapper<>()
    .set("status", PromotionStatusEnum.END.name())
    .eq("promotion_id", promotionId);

    //如果结束时间大于当前时间
    // 可以操作为开启状态,活动状态为已结束
    if (pintuan.getEndTime() > DateUtil.getDateline()) {
    //表示可以再次开启,则不处理未成团订单,因为可以开启
    updateWrapper.set("option_status", PintuanOptionEnum.CAN_OPEN.name());
    } else {
    updateWrapper.set("option_status", PintuanOptionEnum.NOTHING.name());

    //查询所有该活动下的未成团订单(未付款,已付款未成团)
    List<PintuanOrder> orderList =
    this.pintuanOrderMapper.selectList(new QueryWrapper<PintuanOrder>()
    .eq("pintuan_id", promotionId)
    .and(e -> {
    e.eq("order_status", PintuanOrderStatus.new_order.name())
    .or().eq("order_status",
    PintuanOrderStatus.wait.name());
    }));

    for (PintuanOrder order : orderList) {
    pintuanOrderManager.handle(order.getOrderId());
    }
    }

    this.pintuanMapper.update(null, updateWrapper);

    //删除拼团商品索引信息
    pintuanGoodsManager.delIndex(promotionId);
    //删除拼团商品脚本信息
    pintuanScriptManager.deleteCacheScript(promotionId, goodsList);
    }
    }
  • 创建和删除拼团活动商品索引信息

    @Service
    public class PintuanGoodsManagerImpl implements PintuanGoodsManager {

    /**
    * 关闭一个活动的促销商品索引
    *
    * @param promotionId
    */
    @Override
    public void delIndex(Long promotionId) {
    pinTuanSearchManager.deleteByPintuanId(promotionId);
    }

    /**
    * 开启一个活动的促销商品索引
    *
    * @param promotionId
    */
    @Override
    public boolean addIndex(Long promotionId) {
    List<PinTuanGoodsVO> pinTuanGoodsVOS = this.all(promotionId);

    boolean hasError = false;

    for (PinTuanGoodsVO pintuanGoods : pinTuanGoodsVOS) {
    boolean result = pinTuanSearchManager.addIndex(pintuanGoods);
    hasError = result && hasError;
    }

    return hasError;

    }
    }
    @Service
    public class PinTuanSearchManagerImpl implements PinTuanSearchManager {

    @Override
    public boolean addIndex(PinTuanGoodsVO pintuanGoods) {
    String goodsName = pintuanGoods.getGoodsName() + " " +
    SkuNameUtil.createSkuName(pintuanGoods.getSpecs());

    try {

    CacheGoods cacheGoods = goodsClient.getFromCache(pintuanGoods.getGoodsId());
    PtGoodsDoc ptGoodsDoc = new PtGoodsDoc();
    ptGoodsDoc.setCategoryId(cacheGoods.getCategoryId());
    ptGoodsDoc.setThumbnail(pintuanGoods.getThumbnail());
    ptGoodsDoc.setSalesPrice(pintuanGoods.getSalesPrice());
    ptGoodsDoc.setOriginPrice(pintuanGoods.getPrice());
    ptGoodsDoc.setGoodsName(goodsName);
    ptGoodsDoc.setBuyCount(pintuanGoods.getSoldQuantity());
    ptGoodsDoc.setGoodsId(pintuanGoods.getGoodsId());
    ptGoodsDoc.setSkuId(pintuanGoods.getSkuId());
    ptGoodsDoc.setPinTuanId(pintuanGoods.getPintuanId());

    this.addIndex(ptGoodsDoc);

    return true;
    } catch (Exception e) {
    logger.error("为拼团商品[" + goodsName + "]生成索引报错", e);
    debugger.log("为拼团商品[" + goodsName + "]生成索引报错",
    StringUtil.getStackTrace(e));
    return false;
    }
    }

    @Override
    public void deleteByPintuanId(Long pinTuanId) {
    DeleteQuery dq = dq();

    dq.setQuery(QueryBuilders.termQuery("pinTuanId", pinTuanId));

    elasticsearchTemplate.delete(dq);
    }

    @Override
    public void addIndex(PtGoodsDoc goodsDoc) {

    //对cat path特殊处理
    CategoryDO categoryDO = goodsClient.getCategory(goodsDoc.getCategoryId());
    goodsDoc.setCategoryPath(HexUtils.encode(categoryDO.getCategoryPath()));

    IndexQuery indexQuery = new IndexQuery();
    String indexName = esConfig.getIndexName() + "_"
    + EsSettings.PINTUAN_INDEX_NAME;
    indexQuery.setIndexName(indexName);
    indexQuery.setType("pintuan_goods");
    indexQuery.setId(goodsDoc.getSkuId().toString());
    indexQuery.setObject(goodsDoc);

    elasticsearchTemplate.index(indexQuery);
    logger.debug("将拼团商品将拼团商品ID[" + goodsDoc.getGoodsId() + "] "
    + goodsDoc + " 写入索引");
    }
    }
  • 创建和删除拼团活动商品脚本引擎信息

    @Service
    public class PintuanScriptManagerImpl implements PintuanScriptManager {

    @Override
    public void createCacheScript(Long promotionId, List<PintuanGoodsDO> goodsList) {
    //如果参与拼团促销活动的商品集合不为空并且集合长度不为0
    if (goodsList != null && goodsList.size() != 0) {
    //获取拼团活动详情
    Pintuan pintuan = this.pintuanManager.getModel(promotionId);
    //批量放入缓存的脚本数据集合
    Map<String, List<PromotionScriptVO>> cacheMap = new HashMap<>();

    //循环参与拼团促销活动的商品集合,创建商品的拼团促销活动脚本数据结构
    for (PintuanGoodsDO goods : goodsList) {

    //缓存key
    String cacheKey = CachePrefix.SKU_PROMOTION.getPrefix()
    + goods.getSkuId();

    //获取拼团活动脚本信息
    PromotionScriptVO scriptVO = new PromotionScriptVO();

    //渲染并读取满减满赠促销活动脚本信息
    String script = renderScript(pintuan.getStartTime().toString(),
    pintuan.getEndTime().toString(),
    goods.getSalesPrice());

    scriptVO.setPromotionScript(script);
    scriptVO.setPromotionId(promotionId);
    scriptVO.setPromotionType(PromotionTypeEnum.PINTUAN.name());
    scriptVO.setIsGrouped(false);
    scriptVO.setPromotionName("拼团活动");
    scriptVO.setSkuId(goods.getSkuId());

    //从缓存中读取脚本信息
    List<PromotionScriptVO> scriptList = (List<PromotionScriptVO>)
    cache.get(cacheKey);
    if (scriptList == null) {
    scriptList = new ArrayList<>();
    }

    scriptList.add(scriptVO);

    cacheMap.put(cacheKey, scriptList);
    }

    //将参与拼团活动商品的促销脚本数据批量放入缓存中
    cache.multiSet(cacheMap);
    }
    }

    @Override
    public void deleteCacheScript(Long promotionId, List<PintuanGoodsDO> goodsList) {
    //如果参与拼团促销活动的商品集合不为空并且集合长度不为0
    if (goodsList != null && goodsList.size() != 0) {

    //需要批量更新的缓存数据集合
    Map<String, List<PromotionScriptVO>> updateCacheMap = new HashMap<>();

    //需要批量删除的缓存key集合
    List<String> delKeyList = new ArrayList<>();

    for (PintuanGoodsDO goods : goodsList) {
    //缓存key
    String cacheKey = CachePrefix.SKU_PROMOTION.getPrefix() +
    goods.getSkuId();

    //从缓存中读取促销脚本缓存
    List<PromotionScriptVO> scriptCacheList = (List<PromotionScriptVO>)
    cache.get(cacheKey);

    if (scriptCacheList != null && scriptCacheList.size() != 0) {

    //循环促销脚本缓存数据集合
    for (PromotionScriptVO script : scriptCacheList) {

    //如果脚本数据的促销活动信息与当前修改的促销活动信息一致,那么就将此信息删除
    if (PromotionTypeEnum.PINTUAN.name()
    .equals(script.getPromotionType())
    && script.getPromotionId().intValue() ==
    promotionId.intValue()) {
    scriptCacheList.remove(script);
    break;
    }
    }

    if (scriptCacheList.size() == 0) {
    delKeyList.add(cacheKey);
    } else {
    updateCacheMap.put(cacheKey, scriptCacheList);
    }
    }
    }

    cache.multiDel(delKeyList);
    cache.multiSet(updateCacheMap);
    }

    }

    /**
    * 渲染并读取拼团促销活动脚本信息
    * @param startTime 活动开始时间
    * @param endTime 活动结束时间
    * @param price 拼团商品价格
    * @return
    */
    private String renderScript(String startTime, String endTime, Double price) {
    Map<String, Object> model = new HashMap<>();

    Map<String, Object> params = new HashMap<>();
    params.put("startTime", startTime);
    params.put("endTime", endTime);
    params.put("price", price);

    model.put("promotionActive", params);

    String path = "single_promotion.ftl";
    String script = ScriptUtil.renderScript(path, model);

    logger.debug("生成拼团促销活动脚本:" + script);

    return script;
    }
    }
  • 拼团促销活动脚本引擎模板文件—single_promotion.ftl

    <#--
    验证促销活动是否在有效期内
    @param promotionActive 活动信息对象(内置常量)
    .startTime 获取开始时间
    .endTime 活动结束时间
    @param $currentTime 当前时间(变量)
    @returns {boolean}
    -->
    function validTime(){
    if (${promotionActive.startTime} <= $currentTime && $currentTime <= ${promotionActive.endTime}) {
    return true;
    }
    return false;
    }

    <#--
    活动金额计算
    @param promotionActive 活动信息对象(内置常量)
    .price 商品促销活动价格
    @param $sku 商品SKU信息对象(变量)
    .$num 商品数量
    @returns {*}
    -->
    function countPrice() {
    var resultPrice = $sku.$num * ${promotionActive.price};
    return resultPrice < 0 ? 0 : resultPrice.toString();
    }