后台-系统设置-扩展变量-手机广告位-内容正文顶部 |
一、索引设计
参与拼团活动的商品会创建单独的索引结构
拼团商品索引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
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
//获取拼团促销活动信息详情
Pintuan pintuan = pintuanManager.getModel(pintuanId);
//获取本次操作之前参与拼团活动的商品信息集合
List
Long[] skuIds = new Long[pintuanGoodsList.size()];
for (int i = 0; i < pintuanGoodsList.size(); i++) {
skuIds[i] = pintuanGoodsList.get(i).getSkuId();
}
//拼团促销活动
List
= 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
.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);
}
}
- 平台管理端停止和开启拼团活动
```java
@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
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
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
this.pintuanOrderMapper.selectList(new QueryWrapper
.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
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
//如果参与拼团促销活动的商品集合不为空并且集合长度不为0
if (goodsList != null && goodsList.size() != 0) {
//获取拼团活动详情
Pintuan pintuan = this.pintuanManager.getModel(promotionId);
//批量放入缓存的脚本数据集合
Map
//循环参与拼团促销活动的商品集合,创建商品的拼团促销活动脚本数据结构
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
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
//如果参与拼团促销活动的商品集合不为空并且集合长度不为0
if (goodsList != null && goodsList.size() != 0) {
//需要批量更新的缓存数据集合
Map
//需要批量删除的缓存key集合
List
for (PintuanGoodsDO goods : goodsList) {
//缓存key
String cacheKey = CachePrefix.SKU_PROMOTION.getPrefix() +
goods.getSkuId();
//从缓存中读取促销脚本缓存
List
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
Map
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();
}
根据以上内容可以了解到拼团促销活动的索引设计与代码设计,想了解更多详情,可以持续关注易族智汇javashop。
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。