@TOCJavaShop项目总结
总结一下,在JavaShop中所做的功能修改。
商铺后台的活动时间,在活动开始后,也可以进行更改。
@PutMapping(value = "/{id}")
@ApiOperation(value = "修改满优惠活动", response = FullDiscountDO.class)
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "int", paramType = "path")
})
public FullDiscountVO edit(@Valid @RequestBody FullDiscountVO fullDiscountVO, @PathVariable Integer id) {
fullDiscountVO.setFdId(id);
this.verifyFullDiscountParam(fullDiscountVO,1);
this.fullDiscountManager.verifyAuth(id);
this.fullDiscountManager.edit(fullDiscountVO,id);
return fullDiscountVO;
}
在调用修改方法之前,使用this.verifyFullDiscountParam(fullDiscountVO,1);
对满优惠活动的参数信息进行验证;使用this.fullDiscountManager.verifyAuth(id);
来验证操作权限,判断用户是否越权操作。
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {
RuntimeException.class, Exception.class, ServiceException.class, NoPermissionException.class})
public FullDiscountVO edit(FullDiscountVO fullDiscountVO, Integer id) {
//验证活动时间,同一时间只能有一个活动生效
this.verifyTime(fullDiscountVO.getStartTime(), fullDiscountVO.getEndTime(), PromotionTypeEnum.FULL_DISCOUNT, id);
List<PromotionGoodsDTO> goodsDTOList = new ArrayList<>();
//是否是全部商品参与
if (fullDiscountVO.getRangeType() == 1) {
PromotionGoodsDTO goodsDTO = new PromotionGoodsDTO();
goodsDTO.setGoodsId(-1);
goodsDTO.setGoodsName("全部商品");
goodsDTO.setThumbnail("");
goodsDTOList.add(goodsDTO);
fullDiscountVO.setGoodsList(goodsDTOList);
}
this.verifyRule(fullDiscountVO.getGoodsList());
// 获取当前登录的店铺ID
Seller seller = UserContext.getSeller();
Integer sellerId = seller.getSellerId();
fullDiscountVO.setSellerId(sellerId);
FullDiscountDO fullDiscountDO = new FullDiscountDO();
BeanUtils.copyProperties(fullDiscountVO, fullDiscountDO);
this.daoSupport.update(fullDiscountDO, id);
//删除之前的活动与商品的对照关系
PromotionDetailDTO detailDTO = new PromotionDetailDTO();
detailDTO.setStartTime(fullDiscountVO.getStartTime());
detailDTO.setEndTime(fullDiscountVO.getEndTime());
detailDTO.setActivityId(fullDiscountVO.getFdId());
detailDTO.setPromotionType(PromotionTypeEnum.FULL_DISCOUNT.name());
detailDTO.setTitle(fullDiscountVO.getTitle());
//将活动商品入库
this.promotionGoodsManager.edit(fullDiscountVO.getGoodsList(), detailDTO);
cache.put(PromotionCacheKeys.getFullDiscountKey(fullDiscountVO.getFdId()), fullDiscountDO);
return fullDiscountVO;
}
this.verifyTime(fullDiscountVO.getStartTime(), fullDiscountVO.getEndTime(), PromotionTypeEnum.FULL_DISCOUNT, id);
这里验证活动时间,同一时间只能有一个活动生效。
this.verifyRule(fullDiscountVO.getGoodsList());
判断商品集合是否为空,检测活动与活动之间的规则冲突。
cache.put(PromotionCacheKeys.getFullDiscountKey(fullDiscountVO.getFdId()), fullDiscountDO);
写入缓存。
@PutMapping(value = "/{id}")
@ApiOperation(value = "修改单品立减", response = MinusVO.class)
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "int", paramType = "path")
})
public MinusVO edit(@Valid @RequestBody MinusVO minus, @PathVariable Integer id) {
this.minusManager.verifyAuth(id);
PromotionValid.paramValid(minus.getStartTime(),minus.getEndTime(),minus.getRangeType(),minus.getGoodsList(),1);
minus.setMinusId(id);
this.minusManager.edit(minus,id);
return minus;
}
同满减满赠。
@PutMapping(value = "/{id}")
@ApiOperation(value = "修改第二件半价", response = HalfPriceDO.class)
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "int", paramType = "path")
})
public HalfPriceVO edit(@Valid @RequestBody HalfPriceVO halfPrice, @PathVariable Integer id) {
PromotionValid.paramValid(halfPrice.getStartTime(),halfPrice.getEndTime(),
halfPrice.getRangeType(),halfPrice.getGoodsList(),1);
halfPrice.setHpId(id);
this.halfPriceManager.verifyAuth(id);
this.halfPriceManager.edit(halfPrice,id);
return halfPrice;
}
同上。
@ApiOperation(value = "添加活动")
@PostMapping
public Pintuan add(@Valid Pintuan pintuan) {
return this.pintuanManager.add(pintuan);
}
@ApiOperation(value = "修改活动")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "int", paramType = "path")
})
@PutMapping(value = "/{id}")
public Pintuan edit(@Valid Pintuan activeDO, @PathVariable Integer id) {
this.pintuanManager.edit(activeDO, id);
return activeDO;
}
先看添加活动,
@Override
@Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public Pintuan add(Pintuan pintuan) {
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.tradeDaoSupport.insert(pintuan);
Integer pintuanId = this.tradeDaoSupport.getLastId("es_pintuan");
pintuan.setPromotionId(pintuanId);
//创建活动 启用延时任务
PintuanChangeMsg pintuanChangeMsg = new PintuanChangeMsg();
pintuanChangeMsg.setPintuanId(pintuan.getPromotionId());
pintuanChangeMsg.setOptionType(1);
timeTrigger.add(TimeExecute.PINTUAN_EXECUTER, pintuanChangeMsg, pintuan.getStartTime(), TRIGGER_PREFIX + pintuan.getPromotionId());
pintuan.setPromotionId(this.tradeDaoSupport.getLastId("es_pintuan"));
return pintuan;
}
this.verifyParam(pintuan.getStartTime(), pintuan.getEndTime());
判断开始时间是否小于结束时间。
timeTrigger.add()
创建活动时,使用延时任务。rabbitmq实现
@Override
public void add(String executerName, Object param, Long triggerTime, String uniqueKey) {
if (StringUtil.isEmpty(uniqueKey)) {
uniqueKey = StringUtil.getRandStr(10);
}
//标识任务需要执行
cache.put(RabbitmqTriggerUtil.generate(executerName, triggerTime, uniqueKey), 1);
TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(executerName, param, triggerTime, uniqueKey);
if (logger.isDebugEnabled()) {
logger.debug("定时执行在【" + DateUtil.toString(triggerTime, "yyyy-MM-dd HH:mm:ss") + "】,消费【" + param.toString() + "】");
}
rabbitTemplate.convertAndSend(TimeTriggerConfig.DELAYED_EXCHANGE_XDELAY, TimeTriggerConfig.DELAY_ROUTING_KEY_XDELAY, timeTriggerMsg, message -> {
Long current = DateUtil.getDateline();
//如果执行的延时任务应该是在现在日期之前执行的,那么补救一下,要求系统一秒钟后执行
if (triggerTime < current) {
message.getMessageProperties().setDelay(1000);
} else {
Long time = (triggerTime - current) * 1000 + 5000;
message.getMessageProperties().setHeader("x-delay", time);
}
if (logger.isDebugEnabled()) {
logger.debug("还有【" + message.getMessageProperties().getExpiration() + "】执行任务");
}
return message;
});
}
修改活动时,分为活动开始之前修改和活动进行中修改
@Override
@Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public Pintuan edit(Pintuan pintuan, Integer id) {
//获取拼团活动
Pintuan oldPintaun = this.getModel(id);
//校验拼团是否可以被操作
this.verifyParam(pintuan.getStartTime(), pintuan.getEndTime());
this.tradeDaoSupport.update(pintuan, id);
//如果在活动开始之前修改
if(pintuan.getStatus().equals(PromotionStatusEnum.WAIT.name())) {
PintuanChangeMsg pintuanChangeMsg = new PintuanChangeMsg();
pintuanChangeMsg.setPintuanId(pintuan.getPromotionId());
pintuanChangeMsg.setOptionType(1);
timeTrigger.edit(TimeExecute.PINTUAN_EXECUTER, pintuanChangeMsg, oldPintaun.getStartTime(), pintuan.getStartTime(), TRIGGER_PREFIX + id);
} else {
//活动中,先删除活动的旧关闭任务
timeTrigger.delete(TimeExecute.PINTUAN_EXECUTER,oldPintaun.getEndTime(),"{TIME_TRIGGER}_" + id);
//添加活动的新关闭任务
PintuanChangeMsg pintuanChangeMsg = new PintuanChangeMsg();
pintuanChangeMsg.setPintuanId(id);
pintuanChangeMsg.setOptionType(0);
timeTrigger.add(TimeExecute.PINTUAN_EXECUTER, pintuanChangeMsg, pintuan.getEndTime(), "{TIME_TRIGGER}_" + id);
}
return pintuan;
}
因为在活动开始之后,会在执行开始活动之后,再向MQ中设置一个关闭活动的延时任务。所以,在活动开始之前修改,只需要修改活动时间以及MQ的开始活动延时任务即可;在活动进行中修改,需要先删除旧的关闭活动的延时任务,再向MQ中设置一个新的关闭活动的延时任务。
@Override
public void execute(Object object) {
PintuanChangeMsg pintuanChangeMsg = (PintuanChangeMsg) object;
//如果是要开启活动
if (pintuanChangeMsg.getOptionType() == 1) {
Pintuan pintuan = pintuanManager.getModel(pintuanChangeMsg.getPintuanId());
if (PromotionStatusEnum.WAIT.name().equals(pintuan.getStatus()) ||
(PromotionStatusEnum.END.name().equals(pintuan.getStatus()) && PintuanOptionEnum.CAN_OPEN.name().equals(pintuan.getOptionStatus()))) {
pintuanManager.openPromotion(pintuanChangeMsg.getPintuanId());
//开启活动后,立马设置一个关闭的流程
pintuanChangeMsg.setOptionType(0);
timeTrigger.add(TimeExecute.PINTUAN_EXECUTER, pintuanChangeMsg, pintuan.getEndTime(), "{TIME_TRIGGER}_" + pintuan.getPromotionId());
if (logger.isDebugEnabled()) {
this.logger.debug("活动[" + pintuan.getPromotionName() + "]开始,id=[" + pintuan.getPromotionId() + "]");
}
}
} else {
//拼团活动结束
Pintuan pintuan = pintuanManager.getModel(pintuanChangeMsg.getPintuanId());
if (pintuan.getStatus().equals(PromotionStatusEnum.UNDERWAY.name())) {
pintuanManager.closePromotion(pintuanChangeMsg.getPintuanId());
}
if (logger.isDebugEnabled()) {
this.logger.debug("活动[" + pintuan.getPromotionName() + "]结束,id=[" + pintuan.getPromotionId() + "]");
}
}
PC端信任登录,登录配置功能
com/enation/app/javashop/seller/api/shopconnect/ShopConnectSettingController.java
点击信任登录,查询列表时,先查询该店铺是否配置过,如果没有,先对配置信息进行添加操作,再返回列表。
会员回收站,列表展示、搜索、操作(恢复)。
com/enation/app/javashop/seller/api/member/MemberSellerController.java
@ApiOperation(value = "查询列表", response = Member.class)
@GetMapping(value = "/list")
public Page list(@Valid MemberQueryParam memberQueryParam, @ApiIgnore Integer pageNo,
@ApiIgnore Integer pageSize) {
memberQueryParam.setPageNo(pageNo);
memberQueryParam.setPageSize(pageSize);
return this.memberManager.list(memberQueryParam);
}
@PostMapping(value = "/recovery/{id}")
@ApiOperation(value = "恢复会员")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "要恢复的会员主键", required = true, dataType = "int", paramType = "path")
})
public Member recovery(@PathVariable Integer id) {
Member member = memberManager.getModel(id);
if (member == null) {
throw new ResourceNotFoundException("当前会员不存在");
}
if (member.getDisabled().equals(-1)) {
member.setDisabled(0);
member.setDisabledTime(null);
this.memberManager.edit(member, id);
}
return member;
}
com/enation/app/javashop/buyer/api/trade/OrderBuyerController.java
在列表查询时,修改了SQL语句,关联出订单涉及的商品名称,用于关键字查询。在不改动原有查询条件的基础上,新增了一个关键字字段。
在按标签查询(tag页)时,新增了一个用于小唛店已完成列表查询的case,
XIAO_COMPLETE
原因是小唛店在点击确认收货之后,订单就要显示在已完成列表,而原有易旺铺程序在确认收货之后的订单状态为ROG(已收货)。
还复制了一套商铺后台用到的订单列表查询方法,将原有的已完成(COMPLETE)查询条件做了变更,能查询出COMPLETE和ROG状态的订单,原因是商铺后天列表查询的条件传值即为COMPLETE,不能做出修改。
商户前台商品搜索和店铺搜索功能,让其只能查询出易旺铺的商品和店铺。
实现商品搜索的前提,在添加商品时,按照店铺id查询其店铺类别,并将店铺类别插入到商品表中。com/enation/app/javashop/seller/api/goods/GoodsSellerController.java
@ApiOperation(value = "添加商品", response = GoodsDO.class)
@ApiImplicitParam(name = "goods", value = "商品信息", required = true, dataType = "GoodsDTO", paramType = "body")
@PostMapping
public GoodsDO add(@ApiIgnore @Valid @RequestBody GoodsDTO goods) {
GoodsDO goodsDO = this.goodsManager.add(goods);
return goodsDO;
}
// 店铺类别(易旺铺)
boolQueryBuilder.must(QueryBuilders.termQuery("shopTypeId", "1"));
在商品索引实现的实现类中,找到封装成内存需要格式数据的方法,在索引中加入店铺类别,就可以在查询时,实现只查询易旺铺商品。
2. 店铺
com/enation/app/javashop/buyer/api/shop/ShopBuyerController.java
@ApiOperation(value = "查询店铺列表", response = ShopListVO.class)
@GetMapping("/list")
@ApiImplicitParams({
@ApiImplicitParam(name = "page_no", value = "页码", required = true, dataType = "int", paramType = "query"),
@ApiImplicitParam(name = "page_size", value = "分页数", required = true, dataType = "int", paramType = "query"),
@ApiImplicitParam(name = "name", value = "店铺名称", required = false, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "order", value = "按好评率排序", required = false, dataType = "String", paramType = "query")
})
public Page list(@ApiIgnore @NotNull(message = "页码不能为空") Integer pageNo, @ApiIgnore @NotNull(message = "每页数量不能为空") Integer pageSize, String name, String order) {
ShopParamsVO shopParams = new ShopParamsVO();
shopParams.setPageNo(pageNo);
shopParams.setPageSize(pageSize);
shopParams.setShopName(name);
shopParams.setOrder(order);
return shopManager.listShopBasicInfo(shopParams);
}
在店铺列表查询的SQL语句中,加入店铺类别字段并默认值为1(易旺铺),即可实现功能。
完善平台后台的店铺菜单权限功能,实现店铺菜单权限的添加修改和删除功能。
com/enation/app/javashop/manager/api/shop/ShopMenuAuthController.java
店铺菜单权限,实现的功能是对一类店铺进行菜单权限的配置,关联到店铺类别表。所以,在添加功能时,先对店铺类别进行了添加操作,然后再将菜单权限信息入库。
@Override
public ShopMenuAuthVO add(ShopMenuAuthVO shopMenuAuthVO) {
//添加店铺类别
ShopType shopType = new ShopType();
shopType.setTypeName(shopMenuAuthVO.getShopTypeName());
this.memberDaoSupport.insert(shopType);
//角色信息入库
ShopMenuAuthDO shopMenuAuthDO = new ShopMenuAuthDO();
shopMenuAuthDO.setAuthDescribe(shopMenuAuthVO.getShopTypeName() + "菜单");
shopMenuAuthDO.setShopTypeId(memberDaoSupport.getLastId("es_shop_type"));
shopMenuAuthDO.setAuthIds(JsonUtil.objectToJson(queryShopMenuList()));
this.memberDaoSupport.insert(shopMenuAuthDO);
shopMenuAuthVO.setId(memberDaoSupport.getLastId("es_shop_menu_auth"));
return shopMenuAuthVO;
}
其中用到了 queryShopMenuList() 方法对所有的店铺菜单信息进行了查询。这么做的原因是,如果不在添加时,将所有店铺菜单信息转换成Json串格式插入到数据库中,那么就不能进行修改操作,前台会出现异常。
/**
* 查询店铺菜单信息集合
*
* @param
* @return List
*/
private List<ShopMenus> queryShopMenuList() {
String sql = "SELECT id,parent_id,title,identifier,auth_regular,delete_flag,path,grade FROM es_shop_menu WHERE delete_flag = 0";
//店铺菜单信息集合
List<ShopMenu> rootShopMenuList = this.memberDaoSupport.queryForList(sql, ShopMenu.class);
//要返回的结果店铺菜单信息集合
List<ShopMenus> shopMenusList = new ArrayList<>();
//先找到所有的一级菜单
if(rootShopMenuList != null && rootShopMenuList.size() > 0) {
//遍历所有菜单集合
for(int i = 0 ; i < rootShopMenuList.size() ; i++) {
//一级菜单没有父id
if(rootShopMenuList.get(i).getParentId() == 0) {
ShopMenus shopMenus = new ShopMenus();
shopMenus.setId(rootShopMenuList.get(i).getId());
shopMenus.setTitle(rootShopMenuList.get(i).getTitle());
shopMenus.setIdentifier(rootShopMenuList.get(i).getIdentifier());
shopMenus.setChecked(false);
shopMenus.setAuthRegular(rootShopMenuList.get(i).getAuthRegular());
//将菜单加入集合
shopMenusList.add(shopMenus);
}
}
//为一级菜单设置子菜单
if(shopMenusList != null && shopMenusList.size() > 0) {
for(ShopMenus shopMenus : shopMenusList) {
shopMenus.setChildren(getMenuChild(shopMenus.getId(), rootShopMenuList));
}
}
}
return shopMenusList;
}
/**
* 递归查找子菜单
*
* @param menuId,rootShopMenuList
* @return List
*/
private List<ShopMenus> getMenuChild(Integer menuId, List<ShopMenu> rootShopMenuList) {
//子菜单
List<ShopMenus> childList = new ArrayList<ShopMenus>();
for(ShopMenu shopMenu : rootShopMenuList) {
if(shopMenu.getParentId() == menuId) {
ShopMenus shopMenus = new ShopMenus();
shopMenus.setId(shopMenu.getId());
shopMenus.setTitle(shopMenu.getTitle());
shopMenus.setIdentifier(shopMenu.getIdentifier());
shopMenus.setChecked(false);
shopMenus.setAuthRegular(shopMenu.getAuthRegular());
//将子菜单加入集合
childList.add(shopMenus);
}
}
// 把子菜单的子菜单再循环一遍
if(childList.size() > 0) {
for(ShopMenus shopMenus : childList) {
//递归
shopMenus.setChildren(getMenuChild(shopMenus.getId(), rootShopMenuList));
}
}
//递归退出条件
if(childList.size() == 0) {
return null;
}
return childList;
}