需求分析
- 在分享源码之前,先将b2b2c系统中促销模块需求整理、明确,方便源码的理解。
业务需求
- b2b2c电子商务系统中促销活动相关规则需以脚本数据的方式存放至redis缓存,在购物车与结算页面计算商品价格时从redis缓存中获取促销规则信息,实现商品价格的计算。
技术需求
- 促销规则脚本需要使用freemarker模板引擎,需向其中设置内置变量。
- 渲染脚本和调用脚本的方法放入工具类中,方便随时调用。
架构思路
一、脚本生成规则
1、需要生成脚本引擎的促销活动包括:满减满赠、单品立减、第二件半价、团购、限时抢购、拼团、优惠券和积分兑换。
2、根据促销活动规则的不同,生成脚本引擎的时机也不同,大致可分为四类:
第一类:满减满赠、单品立减、第二件半价和优惠券,这四种是在活动生效时生成脚本。需要设置延时任务,活动生效自动生成脚本。
第二类:拼团,由于拼团活动生效后,也可以再次添加或修改参与拼团活动的商品,并且平台可以关闭和开启拼团活动,因此与第一类稍有不同,除活动生效时需要生成脚本外,上述这些操作也要生成或更新脚本。
第三类:团购、限时抢购,这两种促销活动是平台发布商家选择商品进行参与的,参与的商品需要商家进行审核,因此是在审核通过时生成脚本。
第四类:积分兑换,积分兑换针对的是商品,因此是在商家新增和修改商品信息时,生成或更新脚本。
3、促销活动生成的脚本都需要放入缓存中,以便于减少查库操作。
4、清除缓存中无用的脚本引擎:除积分兑换外,其他促销活动都需要利用延时任务,在促销活动失效时,将缓存中的脚本数据清除掉。积分兑换在商家关闭商品的积分兑换操作时才对缓存中的脚本数据进行删除。
二、脚本生成流程图

三、缓存数据结构
1、根据促销活动的不同规则,分为三种缓存数据结构,分别是:SKU级别缓存、店铺级别缓存和优惠券级别缓存。
2、结构图:
SKU级别缓存结构和店铺级别缓存结构级别一致,如下:

而优惠券级别的缓存结构如下:

3、缓存结构说明
(1)、SKU级别缓存:
缓存key:{SKUPROMOTION} 加上SKU的ID,例如:{SKU_PROMOTION}_100。
缓存value:是一个泛型为PromotionScriptVO的List集合。
(2)、店铺级别缓存:
缓存key:{CARTPROMOTION} 加上店铺的ID,例如:{CART_PROMOTION}_100。
缓存value:是一个泛型为PromotionScriptVO的List集合。
(3)、优惠券级别缓存:
缓存key:{COUPONPROMOTION} 加上优惠券的ID,例如:{COUPON_PROMOTION}_100。
缓存value:是一个String类型的脚本字符串。
4、促销活动存储的缓存结构区分
(1)、针对满减满赠、单品立减、第二件半价这三种促销活动,如果商家在发布活动时选择的是全部商品参与,那么则存储的是店铺级别的缓存结构,如果选择的是部分商品参与,那么则存储的是SKU级别的缓存结构。
(2)、针对拼团、团购、显示抢购和积分兑换这些促销活动,都是存储的SKU级别的缓存结构。
(3)、针对优惠券,无论是店铺优惠券还是平台优惠券,存储的都是优惠券级别的缓存结构。
四、脚本规范
1、调用脚本传入的变量规范:
| 变量名称 | 类型 | 说明 |
|---|---|---|
| $currentTime | int | 当前时间,为了验证活动是否有效 |
| $sku | Object | 详见下表 |
| $price | double | 其他优惠活动优惠后总价 |
$sku说明:
| 名称 | 类型 | 说明 |
|---|---|---|
| $price | double | 商品单价 |
| $num | int | 商品数量 |
| $skuId | int | 商品skuID |
| $totalPrice | double | 商品小计(单价*数量) |
2、各个促销活动脚本中的方法说明
满减满赠、优惠券促销活动脚本方法
| 方法名 | 参数 | 返回值类型 | 返回值示例 | 说明 |
|---|---|---|---|---|
| validTime | $currentTime | Boolean | true/false | |
| countPrice | $price | Double | 100.00 | |
| giveGift | $price | Object | [{“type”:“freeShip”,“value”:true},{“type”:“point”,“value”:100},{“type”:“gift”,“value”:10},{“type”:“coupon”,“value”:20}] | 优惠券脚本没有此方法 |
单品立减、第二件半价、团购、限时抢购、团购活动脚本方法
| 方法名 | 参数 | 返回值类型 | 返回值示例 |
|---|---|---|---|
| validTime | $currentTime | Boolean | true/false |
| countPrice | $price | Double | 100.00 |
积分兑换活动脚本方法
| 方法名 | 参数 | 返回值类型 | 返回值示例 | 说明 |
|---|---|---|---|---|
| validTime | $currentTime | Boolean | true/false | 此方法会直接返回true,积分兑换不涉及有效期,脚本中有此方法是为了脚本内容统一 |
| countPrice | $price | Double | 100.00 | |
| countPoint | $price | Integer | 50 |
源码分享
由于促销活动类型较多,此处只以团购活动为例进行相关代码的分享。
ScriptUtil
促销脚本渲染与调用工具类
import com.enation.app.javashop.framework.logs.Logger;
import com.enation.app.javashop.framework.logs.LoggerFactory;
import freemarker.template.Configuration;
import freemarker.template.Template;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
/**
* 脚本生成工具类
* @author duanmingyu
* @version v1.0
* @since v7.2.0
* @date 2020-01-06
*/
public class ScriptUtil {
private static final Logger log = LoggerFactory.getLogger(ScriptUtil.class);
/**
* 渲染并读取脚本内容
* @param name 脚本模板名称(例:test.js,test.html,test.ftl等)
* @param model 渲染脚本需要的数据内容
* @return
*/
public static String renderScript(String name, Map<String, Object> model) {
StringWriter stringWriter = new StringWriter();
try {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
cfg.setClassLoaderForTemplateLoading(Thread.currentThread().getContextClassLoader(),"/script_tpl");
cfg.setDefaultEncoding("UTF-8");
cfg.setNumberFormat("#.##");
Template temp = cfg.getTemplate(name);
temp.process(model, stringWriter);
stringWriter.flush();
return stringWriter.toString();
} catch (Exception e) {
log.error(e.getMessage());
} finally {
try {
stringWriter.close();
} catch (IOException ex) {
log.error(ex.getMessage());
}
}
return null;
}
/**
* @Description:执行script脚本
* @param method script方法名
* @param params 参数
* @param script 脚本
* @return: 返回执行结果
* @Author: liuyulei
* @Date: 2020/1/7
*/
public static Object executeScript(String method,Map<String,Object> params,String script) {
if (StringUtil.

本文介绍了一种基于脚本引擎的b2b2c电子商务系统促销模块架构,详细阐述了不同促销活动的脚本生成规则及缓存策略,以及如何使用Freemarker模板引擎和JavaScript脚本来动态计算商品价格。
最低0.47元/天 解锁文章
1160

被折叠的 条评论
为什么被折叠?



