拼团
一、功能说明
1、拼团促销活动属于商家店铺可直接发布的促销活动。
2、同一个商家在同一时间段内允许创建多个拼团促销活动。
3、同一个商品在同一时间段内只允许参与一个拼团促销活动。
4、正在进行的拼团活动,商家也可以进行修改和删除,不影响已经形成的订单(包括未付款的)。
5、商家修改或删除正在进行的拼团活动对拼团商品的影响:
删除操作:刷新当前活动商品页,会提示该活动商品不存在或已失效 修改活动日期操作:刷新当前活动商品页,会提示该活动商品不存在或已失效 修改活动商品价格:刷新当前活动商品页显示为修改后的价格
6、商家修改或删除正在进行的拼团活动对创建订单的影响:
如果客户已经在拼团的结算页,此时商家 修改了商品拼团价格,然后客户又在结算页直接创建订单,此时价格按修改后的商品价格计算
如果客户已经在拼团的结算页,此时商家删除了此商品(取消此商品参加),然后客户又在结算页直接创建订单 ,则会提示:“此活动已经取消拼团活动,不能发起拼团”
7、商家账号不能参与自己创建的拼团活动。
8、同一个用户可以对同一个拼团商品发起多次拼团。
9、平台管理端可以关闭正在进行的商家拼团促销活动。
关闭活动只是关闭入口,并不是关闭已经创建的订单
10、拼团活动自动结束时或者手动关闭时会判定虚拟成团情况:
未付款订单变成已取消;
已付款非虚拟变成已取消并生成退款单支持原路退回,则原路退回,不支持则管理员手动退款;
已付款并且虚拟成团,变成已成团待发货状态
二、数据库设计
1、表结构设计
拼团促销活动表—es_pintuan
字段名 类型与长度 说明 promotion_id bigint(20) 主键ID promotion_name varchar(255) 活动名称 promotion_title varchar(50) 活动标题 start_time bigint(20) 活动开始时间(存放的是以秒为单位的时间戳) end_time bigint(20) 活动结束时间(存放的是以秒为单位的时间戳) required_num int(10) 成团人数 limit_num int(10) 限购数量(如果为0则代表不限购) enable_mocker smallint(1) 是否开启了虚拟成团 0:否,1:是 create_time bigint(20) 活动创建时间 status varchar(50) 活动状态 WAIT:等待开始,UNDERWAY:进行中,END:已结束 option_status varchar(50) API请求操作状态 CAN_OPEN:可以开启,CAN_CLOSE:可以关闭,NOTHING:没有什么可以操作 seller_name varchar(255) 活动所属商家名称 seller_id bigint(20) 活动所属商家ID(关联es_shop表)
拼团促销活动商品表—es_pintuan_goods
字段名 类型与长度 说明 id bigint(20) 主键ID sku_id bigint(20) 商品SKU(关联es_goods_sku表) goods_name varchar(255) 商品名称 origin_price decimal(20,2) 商品原价格 sales_price decimal(20,2) 商品拼团价格 sn varchar(50) 商品编号 sold_quantity int(10) 商品已售数量 locked_quantity int(10) 商品待发货数量 pintuan_id bigint(20) 拼团活动ID(关联es_pintuan表) goods_id bigint(20) 商品ID(关联es_goods表) specs longtext 商品规格信息 seller_id bigint(20) 商品所属商家ID(关联es_shop表) seller_name varchar(255) 商品所属商家名称 thumbnail varchar(255) 商品缩略图
拼团活动订单表—es_pintuan_order
字段名 类型与长度 说明 order_id bigint(20) 主键ID pintuan_id bigint(20) 拼团活动ID(关联es_pintuan表) end_time bigint(20) 活动结束时间(存放的是以秒为单位的时间戳) sku_id bigint(20) 商品SKU(关联es_goods_sku表) required_num int(10) 成团人数 offered_num int(10) 已参团人数 offered_persons longtext 参团人信息 order_status varchar(50) 订单状态 new_order:新订单,wait:待成团,pay_off:已付款,formed:已成团,cancel:已取消 goods_id bigint(20) 商品ID(关联es_goods表) thumbnail varchar(255) 商品缩略图 goods_name varchar(255) 商品名称
拼团活动子订单表—es_pintuan_child_order
字段名 类型与长度 说明 child_order_id bigint(20) 主键ID order_sn varchar(50) 订单编号(关联es_order表) member_id bigint(20) 会员ID(关联es_member表) sku_id bigint(20) 商品SKU(关联es_goods_sku表) pintuan_id bigint(20) 拼团活动ID(关联es_pintuan表) order_status varchar(50) 订单状态 new_order:新订单,wait:待成团,pay_off:已付款,formed:已成团,cancel:已取消 order_id bigint(20) 主订单ID(关联es_pintuan_order表) member_name varchar(50) 会员名称 origin_price decimal(20,2) 订单原价 sales_price decimal(20,2) 订单拼团价格
2、表关联说明
拼团活动相关表与表关联图
拼团相关表与表之间的关联字段对照
拼团促销活动表(es_pintuan) 拼团活动商品表(es_pintuan_goods) promotion_id pintuan_id 拼团促销活动表(es_pintuan) 拼团活动订单表(es_pintuan_order) promotion_id pintuan_id 拼团促销活动表(es_pintuan) 拼团活动子订单表(es_pintuan_child_order) promotion_id pintuan_id 拼团活动订单表(es_pintuan_order) 拼团活动子订单表(es_pintuan_child_order) order_id order_id 拼团活动子订单表(es_pintuan_child_order) 订单表(es_order) order_sn sn
三、缓存设计
拼团促销活动脚本引擎
脚本引擎缓存结构:
拼团促销活动的促销脚本引擎缓存结构只有一种:SKU级别的缓存结构。
脚本引擎生成和删除时机:
生成:拼团活动开始时生成。
如果活动开始了,商家又新增了参与活动的商品,此时也会更新脚本信息。
删除:拼团活动结束时删除。
关于促销脚本引擎缓存结构可参考《促销活动脚本引擎生成架构》这篇文档。
四、索引设计
参与拼团活动的商品会创建单独的索引结构
拼团商品索引Mapping信息如下:
也就是说,拼团商品在生成索引的时候,只会将如上的商品信息放入到ES中。
五、代码设计
1、接口调用流程图
商家发布促销活动
添加参与促销活动的商品
平台管理端对拼团活动的操作
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();
}