增加商品批发销售模式的处理。优化生成商品sku的结构。
This commit is contained in:
parent
e9fdec6bb0
commit
acb9e78a36
@ -1,7 +1,10 @@
|
||||
/** 增加签到日期 **/
|
||||
ALTER TABLE li_member_sign ADD day int DEFAULT NULL COMMENT '签到日 ';
|
||||
ALTER TABLE li_member_sign DROP INDEX uk_member_day;
|
||||
ALTER TABLE li_member_sign add unique uk_member_day (member_id, day) COMMENT 'uk_member_day';
|
||||
ALTER TABLE li_member_sign
|
||||
ADD day int DEFAULT NULL COMMENT '签到日 ';
|
||||
ALTER TABLE li_member_sign
|
||||
DROP INDEX uk_member_day;
|
||||
ALTER TABLE li_member_sign
|
||||
add unique uk_member_day (member_id, day) COMMENT 'uk_member_day';
|
||||
|
||||
|
||||
|
||||
@ -9,10 +12,35 @@ ALTER TABLE li_member_sign add unique uk_member_day (member_id, day) COMMENT 'uk
|
||||
-- Table structure for li_hot_words_history
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `li_hot_words_history`;
|
||||
CREATE TABLE `li_hot_words_history` (
|
||||
`id` bigint NOT NULL COMMENT 'ID',
|
||||
`create_time` datetime(6) DEFAULT NULL COMMENT '创建时间',
|
||||
`keywords` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '热词',
|
||||
`score` int DEFAULT NULL COMMENT '热词分数',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;
|
||||
CREATE TABLE `li_hot_words_history`
|
||||
(
|
||||
`id` bigint NOT NULL COMMENT 'ID',
|
||||
`create_time` datetime(6) DEFAULT NULL COMMENT '创建时间',
|
||||
`keywords` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '热词',
|
||||
`score` int DEFAULT NULL COMMENT '热词分数',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb3 COLLATE = utf8_bin COMMENT '热词历史表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of li_hot_words_history
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for li_wholesale
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `li_wholesale`;
|
||||
CREATE TABLE `li_wholesale`
|
||||
(
|
||||
`id` bigint NOT NULL,
|
||||
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`create_time` datetime(6) DEFAULT NULL,
|
||||
`delete_flag` bit(1) DEFAULT NULL,
|
||||
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`update_time` datetime(6) DEFAULT NULL,
|
||||
`price` decimal(10, 2) DEFAULT NULL COMMENT '价格',
|
||||
`goods_id` bigint DEFAULT NULL COMMENT '商品id',
|
||||
`sku_id` bigint DEFAULT NULL COMMENT '商品skuId',
|
||||
`num` int DEFAULT NULL COMMENT '起购量',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_bin COMMENT '批发规则表';
|
||||
|
||||
|
@ -76,6 +76,10 @@ public enum ResultCode {
|
||||
GOODS_SKU_QUANTITY_ERROR(11011, "商品库存数量不能为负数"),
|
||||
GOODS_SKU_QUANTITY_NOT_ENOUGH(11011, "商品库存不足"),
|
||||
MUST_HAVE_GOODS_SKU(11012, "规格必须要有一个!"),
|
||||
MUST_HAVE_SALES_MODEL(11022, "销售模式为批发时必须要有批发规则!"),
|
||||
|
||||
HAVE_INVALID_SALES_MODEL(11023, "批发规则存在小于等于0的无效数据!"),
|
||||
|
||||
GOODS_PARAMS_ERROR(11013, "商品参数错误,刷新后重试"),
|
||||
PHYSICAL_GOODS_NEED_TEMP(11014, "实物商品需选择配送模板"),
|
||||
VIRTUAL_GOODS_NOT_NEED_TEMP(11015, "虚拟商品无需选择配送模板"),
|
||||
|
@ -12,7 +12,6 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
|
||||
@ -114,6 +113,9 @@ public class DraftGoods extends BaseEntity {
|
||||
@ApiModelProperty(value = "是否为推荐商品")
|
||||
private boolean recommend;
|
||||
|
||||
/**
|
||||
* @see cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum
|
||||
*/
|
||||
@ApiModelProperty(value = "销售模式")
|
||||
private String salesModel;
|
||||
|
||||
|
@ -8,6 +8,7 @@ import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsTypeEnum;
|
||||
import cn.lili.mybatis.BaseEntity;
|
||||
@ -133,6 +134,9 @@ public class Goods extends BaseEntity {
|
||||
@ApiModelProperty(value = "是否为推荐商品", required = true)
|
||||
private Boolean recommend;
|
||||
|
||||
/**
|
||||
* @see cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum
|
||||
*/
|
||||
@ApiModelProperty(value = "销售模式", required = true)
|
||||
private String salesModel;
|
||||
|
||||
@ -179,10 +183,10 @@ public class Goods extends BaseEntity {
|
||||
if (!sku.containsKey("sn") || sku.get("sn") == null) {
|
||||
throw new ServiceException(ResultCode.GOODS_SKU_SN_ERROR);
|
||||
}
|
||||
if (!sku.containsKey("price") || StringUtil.isEmpty(sku.get("price").toString()) || Convert.toDouble(sku.get("price")) <= 0) {
|
||||
if ((!sku.containsKey("price") || StringUtil.isEmpty(sku.get("price").toString()) || Convert.toDouble(sku.get("price")) <= 0) && !goodsOperationDTO.getSalesModel().equals(GoodsSalesModeEnum.WHOLESALE.name())) {
|
||||
throw new ServiceException(ResultCode.GOODS_SKU_PRICE_ERROR);
|
||||
}
|
||||
if (!sku.containsKey("cost") || StringUtil.isEmpty(sku.get("cost").toString()) || Convert.toDouble(sku.get("cost")) <= 0) {
|
||||
if ((!sku.containsKey("cost") || StringUtil.isEmpty(sku.get("cost").toString()) || Convert.toDouble(sku.get("cost")) <= 0) && !goodsOperationDTO.getSalesModel().equals(GoodsSalesModeEnum.WHOLESALE.name())) {
|
||||
throw new ServiceException(ResultCode.GOODS_SKU_COST_ERROR);
|
||||
}
|
||||
//虚拟商品没有重量字段
|
||||
|
@ -9,6 +9,7 @@ import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
@ -24,6 +25,7 @@ import java.util.Date;
|
||||
@Data
|
||||
@TableName("li_goods_sku")
|
||||
@ApiModel(value = "商品sku对象")
|
||||
@NoArgsConstructor
|
||||
public class GoodsSku extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 4865908658161118934L;
|
||||
@ -150,6 +152,9 @@ public class GoodsSku extends BaseEntity {
|
||||
@ApiModelProperty(value = "是否为推荐商品", required = true)
|
||||
private Boolean recommend;
|
||||
|
||||
/**
|
||||
* @see cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum
|
||||
*/
|
||||
@ApiModelProperty(value = "销售模式", required = true)
|
||||
private String salesModel;
|
||||
/**
|
||||
@ -173,4 +178,36 @@ public class GoodsSku extends BaseEntity {
|
||||
return super.getUpdateTime();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置规格商品的基本商品信息
|
||||
*
|
||||
* @param goods 基本商品信息
|
||||
*/
|
||||
public GoodsSku(Goods goods) {
|
||||
//商品基本信息
|
||||
this.goodsId = goods.getId();
|
||||
this.goodsName = goods.getGoodsName();
|
||||
this.goodsType = goods.getGoodsType();
|
||||
|
||||
this.selfOperated = goods.getSelfOperated();
|
||||
this.sellingPoint = goods.getSellingPoint();
|
||||
this.categoryPath = goods.getCategoryPath();
|
||||
this.brandId = goods.getBrandId();
|
||||
this.marketEnable = goods.getMarketEnable();
|
||||
this.intro = goods.getIntro();
|
||||
this.mobileIntro = goods.getMobileIntro();
|
||||
this.goodsUnit = goods.getGoodsUnit();
|
||||
this.grade = 100D;
|
||||
//商品状态
|
||||
this.authFlag = goods.getAuthFlag();
|
||||
this.salesModel = goods.getSalesModel();
|
||||
//卖家信息
|
||||
this.storeId = goods.getStoreId();
|
||||
this.storeName = goods.getStoreName();
|
||||
this.storeCategoryPath = goods.getStoreCategoryPath();
|
||||
this.freightTemplateId = goods.getTemplateId();
|
||||
this.recommend = goods.getRecommend();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.lili.modules.goods.entity.dos;
|
||||
|
||||
import cn.lili.mybatis.BaseIdEntity;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2022/5/20
|
||||
**/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("li_wholesale")
|
||||
@ApiModel(value = "批发商品")
|
||||
public class Wholesale extends BaseIdEntity {
|
||||
|
||||
private static final long serialVersionUID = -6389806138583086068L;
|
||||
|
||||
@ApiModelProperty(value = "商品ID")
|
||||
private String goodsId;
|
||||
@ApiModelProperty(value = "SkuID")
|
||||
private String skuId;
|
||||
@ApiModelProperty(value = "数量")
|
||||
private Integer num;
|
||||
@ApiModelProperty(value = "金额")
|
||||
private Double price;
|
||||
}
|
@ -13,7 +13,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 商品编辑DTO
|
||||
* 商品操作DTO
|
||||
*
|
||||
* @author pikachu
|
||||
* @since 2020-02-24 19:27:20
|
||||
@ -77,13 +77,12 @@ public class GoodsOperationDTO implements Serializable {
|
||||
@Min(value = 0, message = "运费模板值不正确")
|
||||
private String templateId;
|
||||
|
||||
@ApiModelProperty(value = "sku列表")
|
||||
@Valid
|
||||
private List<Map<String, Object>> skuList;
|
||||
|
||||
@ApiModelProperty(value = "卖点")
|
||||
private String sellingPoint;
|
||||
|
||||
/**
|
||||
* @see cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum
|
||||
*/
|
||||
@ApiModelProperty(value = "销售模式", required = true)
|
||||
private String salesModel;
|
||||
|
||||
@ -112,6 +111,17 @@ public class GoodsOperationDTO implements Serializable {
|
||||
@ApiModelProperty(value = "商品视频")
|
||||
private String goodsVideo;
|
||||
|
||||
|
||||
@ApiModelProperty(value = "sku列表")
|
||||
@Valid
|
||||
private List<Map<String, Object>> skuList;
|
||||
|
||||
/**
|
||||
* 批发商品规则
|
||||
*/
|
||||
@ApiModelProperty(value = "批发商品规则")
|
||||
private List<WholesaleDTO> wholesaleList;
|
||||
|
||||
public String getGoodsName() {
|
||||
//对商品对名称做一个极限处理。这里没有用xss过滤是因为xss过滤为全局过滤,影响很大。
|
||||
// 业务中,全局代码中只有商品名称不能拥有英文逗号,是由于商品名称存在一个数据库联合查询,结果要根据逗号分组
|
||||
|
@ -77,6 +77,12 @@ public class GoodsSearchParams extends PageVO {
|
||||
@ApiModelProperty(value = "商品类型")
|
||||
private String goodsType;
|
||||
|
||||
/**
|
||||
* @see cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum
|
||||
*/
|
||||
@ApiModelProperty(value = "销售模式", required = true)
|
||||
private String salesModel;
|
||||
|
||||
public <T> QueryWrapper<T> queryWrapper() {
|
||||
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
|
||||
if (CharSequenceUtil.isNotEmpty(goodsId)) {
|
||||
@ -121,6 +127,9 @@ public class GoodsSearchParams extends PageVO {
|
||||
if (CharSequenceUtil.isNotEmpty(goodsType)) {
|
||||
queryWrapper.eq("goods_type", goodsType);
|
||||
}
|
||||
if (CharSequenceUtil.isNotEmpty(salesModel)) {
|
||||
queryWrapper.eq("sales_model", salesModel);
|
||||
}
|
||||
|
||||
queryWrapper.eq("delete_flag", false);
|
||||
this.betweenWrapper(queryWrapper);
|
||||
|
@ -0,0 +1,16 @@
|
||||
package cn.lili.modules.goods.entity.dto;
|
||||
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2022/5/25
|
||||
**/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WholesaleDTO extends Wholesale {
|
||||
private static final long serialVersionUID = 853297561151783335L;
|
||||
|
||||
}
|
@ -1,19 +1,14 @@
|
||||
package cn.lili.modules.goods.entity.enums;
|
||||
|
||||
/**
|
||||
* 商品审核
|
||||
* 销售模式
|
||||
*
|
||||
* @author pikachu
|
||||
* @since 2020-02-26 23:24:13
|
||||
*/
|
||||
public enum GoodsSalesModeEnum {
|
||||
/**
|
||||
* 需要审核 并且待审核
|
||||
*/
|
||||
|
||||
RETAIL("零售"),
|
||||
/**
|
||||
* 审核通过
|
||||
*/
|
||||
WHOLESALE("批发");
|
||||
|
||||
private final String description;
|
||||
|
@ -1,9 +1,11 @@
|
||||
package cn.lili.modules.goods.entity.vos;
|
||||
|
||||
import cn.lili.modules.goods.entity.dos.Goods;
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsParamsDTO;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -14,6 +16,7 @@ import java.util.List;
|
||||
* @since 2020-02-26 23:24:13
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GoodsVO extends Goods {
|
||||
|
||||
private static final long serialVersionUID = 6377623919990713567L;
|
||||
@ -29,4 +32,7 @@ public class GoodsVO extends Goods {
|
||||
|
||||
@ApiModelProperty(value = "sku列表")
|
||||
private List<GoodsSkuVO> skuList;
|
||||
|
||||
@ApiModelProperty(value = "批发商品消费规则列表")
|
||||
private List<Wholesale> wholesaleList;
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package cn.lili.modules.goods.mapper;
|
||||
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2022/5/24
|
||||
**/
|
||||
public interface WholesaleMapper extends BaseMapper<Wholesale> {
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.lili.modules.goods.render;
|
||||
|
||||
import cn.lili.modules.goods.entity.dos.GoodsSku;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 商品sku渲染器
|
||||
*
|
||||
* @author paulG
|
||||
* @since 2022/5/20
|
||||
**/
|
||||
public interface GoodsSkuRender {
|
||||
|
||||
|
||||
GoodsSku render(List<Map<String, Object>> skuList);
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package cn.lili.modules.goods.service;
|
||||
import cn.lili.cache.CachePrefix;
|
||||
import cn.lili.modules.goods.entity.dos.Goods;
|
||||
import cn.lili.modules.goods.entity.dos.GoodsSku;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsSkuStockDTO;
|
||||
import cn.lili.modules.goods.entity.vos.GoodsSkuVO;
|
||||
@ -43,19 +44,18 @@ public interface GoodsSkuService extends IService<GoodsSku> {
|
||||
/**
|
||||
* 添加商品sku
|
||||
*
|
||||
* @param skuList sku列表
|
||||
* @param goods 商品信息
|
||||
* @param goods 商品信息
|
||||
* @param goodsOperationDTO 商品操作信息
|
||||
*/
|
||||
void add(List<Map<String, Object>> skuList, Goods goods);
|
||||
void add(Goods goods, GoodsOperationDTO goodsOperationDTO);
|
||||
|
||||
/**
|
||||
* 更新商品sku
|
||||
*
|
||||
* @param skuList sku列表
|
||||
* @param goods 商品信息
|
||||
* @param regeneratorSkuFlag 是否是否重新生成sku
|
||||
* @param goods 商品信息
|
||||
* @param goodsOperationDTO 商品操作信息
|
||||
*/
|
||||
void update(List<Map<String, Object>> skuList, Goods goods, Boolean regeneratorSkuFlag);
|
||||
void update(Goods goods, GoodsOperationDTO goodsOperationDTO);
|
||||
|
||||
/**
|
||||
* 更新商品sku
|
||||
@ -154,9 +154,9 @@ public interface GoodsSkuService extends IService<GoodsSku> {
|
||||
/**
|
||||
* 更新商品sku状态根据店铺id
|
||||
*
|
||||
* @param storeId 店铺id
|
||||
* @param storeId 店铺id
|
||||
* @param marketEnable 市场启用状态
|
||||
* @param authFlag 审核状态
|
||||
* @param authFlag 审核状态
|
||||
*/
|
||||
void updateGoodsSkuStatusByStoreId(String storeId, String marketEnable, String authFlag);
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
package cn.lili.modules.goods.service;
|
||||
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2022/5/24
|
||||
**/
|
||||
public interface WholesaleService extends IService<Wholesale> {
|
||||
|
||||
|
||||
List<Wholesale> findByGoodsId(String goodsId);
|
||||
|
||||
Boolean removeByGoodsId(String goodsId);
|
||||
|
||||
/**
|
||||
* 匹配批发规则
|
||||
*
|
||||
* @param goodsId 商品规则
|
||||
* @param num 数量
|
||||
* @return 批发规则
|
||||
*/
|
||||
Wholesale match(String goodsId, Integer num);
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package cn.lili.modules.goods.serviceimpl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@ -14,6 +15,7 @@ import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.modules.goods.entity.dos.Category;
|
||||
import cn.lili.modules.goods.entity.dos.Goods;
|
||||
import cn.lili.modules.goods.entity.dos.GoodsGallery;
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsParamsDTO;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
|
||||
@ -22,10 +24,7 @@ import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
|
||||
import cn.lili.modules.goods.entity.vos.GoodsSkuVO;
|
||||
import cn.lili.modules.goods.entity.vos.GoodsVO;
|
||||
import cn.lili.modules.goods.mapper.GoodsMapper;
|
||||
import cn.lili.modules.goods.service.CategoryService;
|
||||
import cn.lili.modules.goods.service.GoodsGalleryService;
|
||||
import cn.lili.modules.goods.service.GoodsService;
|
||||
import cn.lili.modules.goods.service.GoodsSkuService;
|
||||
import cn.lili.modules.goods.service.*;
|
||||
import cn.lili.modules.member.entity.dos.MemberEvaluation;
|
||||
import cn.lili.modules.member.entity.enums.EvaluationGradeEnum;
|
||||
import cn.lili.modules.member.service.MemberEvaluationService;
|
||||
@ -110,6 +109,9 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
|
||||
@Autowired
|
||||
private FreightTemplateService freightTemplateService;
|
||||
|
||||
@Autowired
|
||||
private WholesaleService wholesaleService;
|
||||
|
||||
@Autowired
|
||||
private Cache<GoodsVO> cache;
|
||||
|
||||
@ -168,7 +170,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
|
||||
//添加商品
|
||||
this.save(goods);
|
||||
//添加商品sku信息
|
||||
this.goodsSkuService.add(goodsOperationDTO.getSkuList(), goods);
|
||||
this.goodsSkuService.add(goods, goodsOperationDTO);
|
||||
//添加相册
|
||||
if (goodsOperationDTO.getGoodsGalleryList() != null && !goodsOperationDTO.getGoodsGalleryList().isEmpty()) {
|
||||
this.goodsGalleryService.add(goodsOperationDTO.getGoodsGalleryList(), goods.getId());
|
||||
@ -192,7 +194,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
|
||||
//修改商品
|
||||
this.updateById(goods);
|
||||
//修改商品sku信息
|
||||
this.goodsSkuService.update(goodsOperationDTO.getSkuList(), goods, goodsOperationDTO.getRegeneratorSkuFlag());
|
||||
this.goodsSkuService.update(goods, goodsOperationDTO);
|
||||
//添加相册
|
||||
if (goodsOperationDTO.getGoodsGalleryList() != null && !goodsOperationDTO.getGoodsGalleryList().isEmpty()) {
|
||||
this.goodsGalleryService.add(goodsOperationDTO.getGoodsGalleryList(), goods.getId());
|
||||
@ -247,6 +249,12 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
|
||||
if (CharSequenceUtil.isNotEmpty(goods.getParams())) {
|
||||
goodsVO.setGoodsParamsDTOList(JSONUtil.toList(goods.getParams(), GoodsParamsDTO.class));
|
||||
}
|
||||
|
||||
List<Wholesale> wholesaleList = wholesaleService.findByGoodsId(goodsId);
|
||||
if (CollUtil.isNotEmpty(wholesaleList)) {
|
||||
goodsVO.setWholesaleList(wholesaleList);
|
||||
}
|
||||
|
||||
cache.put(CachePrefix.GOODS.getPrefix() + goodsId, goodsVO);
|
||||
return goodsVO;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.lili.modules.goods.serviceimpl;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
@ -16,6 +15,7 @@ import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.utils.SnowFlake;
|
||||
import cn.lili.modules.goods.entity.dos.Goods;
|
||||
import cn.lili.modules.goods.entity.dos.GoodsSku;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsSkuStockDTO;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
|
||||
@ -26,10 +26,9 @@ import cn.lili.modules.goods.entity.vos.GoodsVO;
|
||||
import cn.lili.modules.goods.entity.vos.SpecValueVO;
|
||||
import cn.lili.modules.goods.event.GeneratorEsGoodsIndexEvent;
|
||||
import cn.lili.modules.goods.mapper.GoodsSkuMapper;
|
||||
import cn.lili.modules.goods.service.CategoryService;
|
||||
import cn.lili.modules.goods.service.GoodsGalleryService;
|
||||
import cn.lili.modules.goods.service.GoodsService;
|
||||
import cn.lili.modules.goods.service.GoodsSkuService;
|
||||
import cn.lili.modules.goods.service.*;
|
||||
import cn.lili.modules.goods.sku.GoodsSkuBuilder;
|
||||
import cn.lili.modules.goods.sku.render.SalesModelRender;
|
||||
import cn.lili.modules.member.entity.dos.FootPrint;
|
||||
import cn.lili.modules.member.entity.dto.EvaluationQueryParams;
|
||||
import cn.lili.modules.member.entity.enums.EvaluationGradeEnum;
|
||||
@ -38,7 +37,6 @@ import cn.lili.modules.promotion.entity.dos.PromotionGoods;
|
||||
import cn.lili.modules.promotion.entity.dto.search.PromotionGoodsSearchParams;
|
||||
import cn.lili.modules.promotion.entity.enums.CouponGetEnum;
|
||||
import cn.lili.modules.promotion.service.PromotionGoodsService;
|
||||
import cn.lili.modules.search.entity.dos.EsGoodsAttribute;
|
||||
import cn.lili.modules.search.entity.dos.EsGoodsIndex;
|
||||
import cn.lili.modules.search.service.EsGoodsIndexService;
|
||||
import cn.lili.modules.search.utils.EsIndexUtil;
|
||||
@ -114,35 +112,42 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
|
||||
@Autowired
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
@Autowired
|
||||
private WholesaleService wholesaleService;
|
||||
|
||||
@Autowired
|
||||
private List<SalesModelRender> salesModelRenders;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void add(List<Map<String, Object>> skuList, Goods goods) {
|
||||
// 检查是否需要生成索引
|
||||
List<GoodsSku> newSkuList;
|
||||
//如果有规格
|
||||
if (skuList != null && !skuList.isEmpty()) {
|
||||
// 添加商品sku
|
||||
newSkuList = this.addGoodsSku(skuList, goods);
|
||||
} else {
|
||||
public void add(Goods goods, GoodsOperationDTO goodsOperationDTO) {
|
||||
// 是否存在规格
|
||||
if (goodsOperationDTO.getSkuList() == null || goodsOperationDTO.getSkuList().isEmpty()) {
|
||||
throw new ServiceException(ResultCode.MUST_HAVE_GOODS_SKU);
|
||||
}
|
||||
// 检查是否需要生成索引
|
||||
List<GoodsSku> goodsSkus = GoodsSkuBuilder.buildBatch(goods, goodsOperationDTO);
|
||||
renderGoodsSkuList(goodsSkus, goodsOperationDTO);
|
||||
|
||||
this.updateStock(newSkuList);
|
||||
if (!newSkuList.isEmpty()) {
|
||||
generateEs(goods);
|
||||
if (!goodsSkus.isEmpty()) {
|
||||
this.saveOrUpdateBatch(goodsSkus);
|
||||
this.updateStock(goodsSkus);
|
||||
this.generateEs(goods);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(List<Map<String, Object>> skuList, Goods goods, Boolean regeneratorSkuFlag) {
|
||||
public void update(Goods goods, GoodsOperationDTO goodsOperationDTO) {
|
||||
// 是否存在规格
|
||||
if (skuList == null || skuList.isEmpty()) {
|
||||
if (goodsOperationDTO.getSkuList() == null || goodsOperationDTO.getSkuList().isEmpty()) {
|
||||
throw new ServiceException(ResultCode.MUST_HAVE_GOODS_SKU);
|
||||
}
|
||||
List<GoodsSku> newSkuList;
|
||||
List<GoodsSku> skuList;
|
||||
//删除旧的sku信息
|
||||
if (Boolean.TRUE.equals(regeneratorSkuFlag)) {
|
||||
if (Boolean.TRUE.equals(goodsOperationDTO.getRegeneratorSkuFlag())) {
|
||||
skuList = GoodsSkuBuilder.buildBatch(goods, goodsOperationDTO);
|
||||
renderGoodsSkuList(skuList, goodsOperationDTO);
|
||||
List<GoodsSkuVO> goodsListByGoodsId = getGoodsListByGoodsId(goods.getId());
|
||||
List<String> oldSkuIds = new ArrayList<>();
|
||||
//删除旧索引
|
||||
@ -154,40 +159,34 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
|
||||
this.remove(new LambdaQueryWrapper<GoodsSku>().eq(GoodsSku::getGoodsId, goods.getId()));
|
||||
//删除sku相册
|
||||
goodsGalleryService.removeByGoodsId(goods.getId());
|
||||
getGoodsListByGoodsId(goods.getId());
|
||||
// 添加商品sku
|
||||
newSkuList = this.addGoodsSku(skuList, goods);
|
||||
|
||||
//发送mq消息
|
||||
String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.SKU_DELETE.name();
|
||||
rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(oldSkuIds), RocketmqSendCallbackBuilder.commonCallback());
|
||||
} else {
|
||||
newSkuList = new ArrayList<>();
|
||||
for (Map<String, Object> map : skuList) {
|
||||
skuList = new ArrayList<>();
|
||||
for (Map<String, Object> map : goodsOperationDTO.getSkuList()) {
|
||||
GoodsSku sku = null;
|
||||
if (map.get("id") != null) {
|
||||
sku = this.getGoodsSkuByIdFromCache(map.get("id").toString());
|
||||
sku = GoodsSkuBuilder.build(this.getGoodsSkuByIdFromCache(map.get("id").toString()), map, goodsOperationDTO);
|
||||
}
|
||||
if (sku == null || map.get("id") == null) {
|
||||
sku = new GoodsSku();
|
||||
sku = GoodsSkuBuilder.build(goods, map, goodsOperationDTO);
|
||||
}
|
||||
//设置商品信息
|
||||
goodsInfo(sku, goods);
|
||||
//设置商品规格信息
|
||||
skuInfo(sku, goods, map, null);
|
||||
newSkuList.add(sku);
|
||||
renderGoodsSku(sku, goodsOperationDTO);
|
||||
skuList.add(sku);
|
||||
//如果商品状态值不对,则es索引移除
|
||||
if (goods.getAuthFlag().equals(GoodsAuthEnum.PASS.name()) && goods.getMarketEnable().equals(GoodsStatusEnum.UPPER.name())) {
|
||||
goodsIndexService.deleteIndexById(sku.getId());
|
||||
this.clearCache(sku.getId());
|
||||
}
|
||||
}
|
||||
this.remove(new LambdaQueryWrapper<GoodsSku>().eq(GoodsSku::getGoodsId, goods.getId()));
|
||||
this.saveOrUpdateBatch(newSkuList);
|
||||
}
|
||||
this.updateStock(newSkuList);
|
||||
if (GoodsAuthEnum.PASS.name().equals(goods.getAuthFlag()) && !newSkuList.isEmpty()) {
|
||||
generateEs(goods);
|
||||
if (!skuList.isEmpty()) {
|
||||
this.remove(new LambdaQueryWrapper<GoodsSku>().eq(GoodsSku::getGoodsId, goods.getId()));
|
||||
this.saveOrUpdateBatch(skuList);
|
||||
this.updateStock(skuList);
|
||||
this.generateEs(goods);
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,9 +256,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
|
||||
}
|
||||
|
||||
//商品下架||商品未审核通过||商品删除,则提示:商品已下架
|
||||
if (GoodsStatusEnum.DOWN.name().equals(goodsVO.getMarketEnable())
|
||||
|| !GoodsAuthEnum.PASS.name().equals(goodsVO.getAuthFlag())
|
||||
|| Boolean.TRUE.equals(goodsVO.getDeleteFlag())) {
|
||||
if (GoodsStatusEnum.DOWN.name().equals(goodsVO.getMarketEnable()) || !GoodsAuthEnum.PASS.name().equals(goodsVO.getAuthFlag()) || Boolean.TRUE.equals(goodsVO.getDeleteFlag())) {
|
||||
throw new ServiceException(ResultCode.GOODS_NOT_EXIST);
|
||||
}
|
||||
|
||||
@ -278,13 +275,10 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
|
||||
promotionMap = promotionMap.entrySet().stream().parallel().filter(i -> {
|
||||
JSONObject jsonObject = JSONUtil.parseObj(i.getValue());
|
||||
// 过滤活动赠送优惠券和无效时间的活动
|
||||
return (jsonObject.get("getType") == null || jsonObject.get("getType", String.class).equals(CouponGetEnum.FREE.name())) &&
|
||||
(jsonObject.get("startTime") != null && jsonObject.get("startTime", Date.class).getTime() <= System.currentTimeMillis()) &&
|
||||
(jsonObject.get("endTime") == null || jsonObject.get("endTime", Date.class).getTime() >= System.currentTimeMillis());
|
||||
return (jsonObject.get("getType") == null || jsonObject.get("getType", String.class).equals(CouponGetEnum.FREE.name())) && (jsonObject.get("startTime") != null && jsonObject.get("startTime", Date.class).getTime() <= System.currentTimeMillis()) && (jsonObject.get("endTime") == null || jsonObject.get("endTime", Date.class).getTime() >= System.currentTimeMillis());
|
||||
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
Optional<Map.Entry<String, Object>> containsPromotion = promotionMap.entrySet().stream().filter(i ->
|
||||
i.getKey().contains(PromotionTypeEnum.SECKILL.name()) || i.getKey().contains(PromotionTypeEnum.PINTUAN.name())).findFirst();
|
||||
Optional<Map.Entry<String, Object>> containsPromotion = promotionMap.entrySet().stream().filter(i -> i.getKey().contains(PromotionTypeEnum.SECKILL.name()) || i.getKey().contains(PromotionTypeEnum.PINTUAN.name())).findFirst();
|
||||
if (containsPromotion.isPresent()) {
|
||||
JSONObject jsonObject = JSONUtil.parseObj(containsPromotion.get().getValue());
|
||||
PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams();
|
||||
@ -305,6 +299,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
|
||||
|
||||
//获取分类
|
||||
String[] split = goodsSkuDetail.getCategoryPath().split(",");
|
||||
map.put("wholesaleList", wholesaleService.findByGoodsId(goodsSkuDetail.getGoodsId()));
|
||||
map.put("categoryName", categoryService.getCategoryNameByIds(Arrays.asList(split)));
|
||||
|
||||
//获取规格信息
|
||||
@ -347,7 +342,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
|
||||
cache.put(GoodsSkuService.getCacheKeys(sku.getId()), sku);
|
||||
}
|
||||
if (!goodsSkus.isEmpty()) {
|
||||
generateEs(goods);
|
||||
this.generateEs(goods);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -565,10 +560,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
|
||||
|
||||
|
||||
//修改规格索引,发送mq消息
|
||||
Map<String, Object> updateIndexFieldsMap = EsIndexUtil.getUpdateIndexFieldsMap(
|
||||
MapUtil.builder(new HashMap<String, Object>()).put("id", goodsSku.getId()).build(),
|
||||
MapUtil.builder(new HashMap<String, Object>()).put("commentNum", goodsSku.getCommentNum()).put("highPraiseNum", highPraiseNum)
|
||||
.put("grade", grade).build());
|
||||
Map<String, Object> updateIndexFieldsMap = EsIndexUtil.getUpdateIndexFieldsMap(MapUtil.builder(new HashMap<String, Object>()).put("id", goodsSku.getId()).build(), MapUtil.builder(new HashMap<String, Object>()).put("commentNum", goodsSku.getCommentNum()).put("highPraiseNum", highPraiseNum).put("grade", grade).build());
|
||||
String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.UPDATE_GOODS_INDEX_FIELD.name();
|
||||
rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(updateIndexFieldsMap), RocketmqSendCallbackBuilder.commonCallback());
|
||||
|
||||
@ -632,153 +624,25 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
|
||||
|
||||
|
||||
/**
|
||||
* 增加sku集合
|
||||
* 批量渲染商品sku
|
||||
*
|
||||
* @param skuList sku列表
|
||||
* @param goods 商品信息
|
||||
* @param goodsSkuList sku集合
|
||||
* @param goodsOperationDTO 商品操作DTO
|
||||
*/
|
||||
List<GoodsSku> addGoodsSku(List<Map<String, Object>> skuList, Goods goods) {
|
||||
List<GoodsSku> skus = new ArrayList<>();
|
||||
for (Map<String, Object> skuVO : skuList) {
|
||||
Map<String, Object> resultMap = this.add(skuVO, goods);
|
||||
GoodsSku goodsSku = (GoodsSku) resultMap.get("goodsSku");
|
||||
if (goods.getSelfOperated() != null) {
|
||||
goodsSku.setSelfOperated(goods.getSelfOperated());
|
||||
}
|
||||
goodsSku.setGoodsType(goods.getGoodsType());
|
||||
skus.add(goodsSku);
|
||||
cache.put(GoodsSkuService.getStockCacheKey(goodsSku.getId()), goodsSku.getQuantity());
|
||||
}
|
||||
this.saveOrUpdateBatch(skus);
|
||||
return skus;
|
||||
void renderGoodsSkuList(List<GoodsSku> goodsSkuList, GoodsOperationDTO goodsOperationDTO) {
|
||||
// 商品销售模式渲染器
|
||||
salesModelRenders.stream().filter(i -> i.getSalesMode().equals(goodsOperationDTO.getSalesModel())).findFirst().ifPresent(i -> i.renderBatch(goodsSkuList, goodsOperationDTO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加商品规格
|
||||
* 渲染商品sku
|
||||
*
|
||||
* @param map 规格属性
|
||||
* @param goods 商品
|
||||
* @return 规格商品
|
||||
* @param goodsSku sku
|
||||
* @param goodsOperationDTO 商品操作DTO
|
||||
*/
|
||||
private Map<String, Object> add(Map<String, Object> map, Goods goods) {
|
||||
Map<String, Object> resultMap = new HashMap<>(2);
|
||||
GoodsSku sku = new GoodsSku();
|
||||
|
||||
//商品索引
|
||||
EsGoodsIndex esGoodsIndex = new EsGoodsIndex();
|
||||
|
||||
//设置商品信息
|
||||
goodsInfo(sku, goods);
|
||||
//设置商品规格信息
|
||||
skuInfo(sku, goods, map, esGoodsIndex);
|
||||
|
||||
esGoodsIndex.setGoodsSku(sku);
|
||||
resultMap.put("goodsSku", sku);
|
||||
resultMap.put("goodsIndex", esGoodsIndex);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置规格商品的商品信息
|
||||
*
|
||||
* @param sku 规格
|
||||
* @param goods 商品
|
||||
*/
|
||||
private void goodsInfo(GoodsSku sku, Goods goods) {
|
||||
//商品基本信息
|
||||
sku.setGoodsId(goods.getId());
|
||||
|
||||
sku.setSellingPoint(goods.getSellingPoint());
|
||||
sku.setCategoryPath(goods.getCategoryPath());
|
||||
sku.setBrandId(goods.getBrandId());
|
||||
sku.setMarketEnable(goods.getMarketEnable());
|
||||
sku.setIntro(goods.getIntro());
|
||||
sku.setMobileIntro(goods.getMobileIntro());
|
||||
sku.setGoodsUnit(goods.getGoodsUnit());
|
||||
sku.setGrade(100D);
|
||||
//商品状态
|
||||
sku.setAuthFlag(goods.getAuthFlag());
|
||||
sku.setSalesModel(goods.getSalesModel());
|
||||
//卖家信息
|
||||
sku.setStoreId(goods.getStoreId());
|
||||
sku.setStoreName(goods.getStoreName());
|
||||
sku.setStoreCategoryPath(goods.getStoreCategoryPath());
|
||||
sku.setFreightTemplateId(goods.getTemplateId());
|
||||
sku.setRecommend(goods.getRecommend());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置商品规格信息
|
||||
*
|
||||
* @param sku 规格商品
|
||||
* @param goods 商品
|
||||
* @param map 规格信息
|
||||
* @param esGoodsIndex 商品索引
|
||||
*/
|
||||
private void skuInfo(GoodsSku sku, Goods goods, Map<String, Object> map, EsGoodsIndex esGoodsIndex) {
|
||||
|
||||
//规格简短信息
|
||||
StringBuilder simpleSpecs = new StringBuilder();
|
||||
//商品名称
|
||||
StringBuilder goodsName = new StringBuilder(goods.getGoodsName());
|
||||
//规格商品缩略图
|
||||
String thumbnail = "";
|
||||
String small = "";
|
||||
//规格值
|
||||
Map<String, Object> specMap = new HashMap<>(16);
|
||||
//商品属性
|
||||
List<EsGoodsAttribute> attributes = new ArrayList<>();
|
||||
|
||||
//获取规格信息
|
||||
for (Map.Entry<String, Object> spec : map.entrySet()) {
|
||||
//保存规格信息
|
||||
if (("id").equals(spec.getKey()) || ("sn").equals(spec.getKey()) || ("cost").equals(spec.getKey())
|
||||
|| ("price").equals(spec.getKey()) || ("quantity").equals(spec.getKey())
|
||||
|| ("weight").equals(spec.getKey())) {
|
||||
continue;
|
||||
} else {
|
||||
specMap.put(spec.getKey(), spec.getValue());
|
||||
if (("images").equals(spec.getKey())) {
|
||||
//设置规格商品缩略图
|
||||
List<Map<String, String>> images = (List<Map<String, String>>) spec.getValue();
|
||||
if (images == null || images.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
//设置规格商品缩略图
|
||||
//如果规格没有图片,则用商品图片复盖。有则增加规格图片,放在商品图片集合之前
|
||||
if (CharSequenceUtil.isNotEmpty(spec.getValue().toString())) {
|
||||
thumbnail = goodsGalleryService.getGoodsGallery(images.get(0).get("url")).getThumbnail();
|
||||
small = goodsGalleryService.getGoodsGallery(images.get(0).get("url")).getSmall();
|
||||
}
|
||||
} else {
|
||||
if (spec.getValue() != null) {
|
||||
//设置商品名称
|
||||
goodsName.append(" ").append(spec.getValue());
|
||||
//规格简短信息
|
||||
simpleSpecs.append(" ").append(spec.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//设置规格信息
|
||||
sku.setGoodsName(goodsName.toString());
|
||||
sku.setThumbnail(thumbnail);
|
||||
sku.setSmall(small);
|
||||
|
||||
//规格信息
|
||||
sku.setId(Convert.toStr(map.get("id"), ""));
|
||||
sku.setSn(Convert.toStr(map.get("sn")));
|
||||
sku.setWeight(Convert.toDouble(map.get("weight"), 0D));
|
||||
sku.setPrice(Convert.toDouble(map.get("price"), 0D));
|
||||
sku.setCost(Convert.toDouble(map.get("cost"), 0D));
|
||||
sku.setQuantity(Convert.toInt(map.get("quantity"), 0));
|
||||
sku.setSpecs(JSONUtil.toJsonStr(specMap));
|
||||
sku.setSimpleSpecs(simpleSpecs.toString());
|
||||
|
||||
if (esGoodsIndex != null) {
|
||||
//商品索引
|
||||
esGoodsIndex.setAttrList(attributes);
|
||||
}
|
||||
void renderGoodsSku(GoodsSku goodsSku, GoodsOperationDTO goodsOperationDTO) {
|
||||
// 商品销售模式渲染器
|
||||
salesModelRenders.stream().filter(i -> i.getSalesMode().equals(goodsOperationDTO.getSalesModel())).findFirst().ifPresent(i -> i.renderSingle(goodsSku, goodsOperationDTO));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,64 @@
|
||||
package cn.lili.modules.goods.serviceimpl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.lili.cache.Cache;
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import cn.lili.modules.goods.mapper.WholesaleMapper;
|
||||
import cn.lili.modules.goods.service.WholesaleService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheConfig;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2022/5/24
|
||||
**/
|
||||
@Service
|
||||
@CacheConfig(cacheNames = "{wholesale}_")
|
||||
public class WholesaleServiceImpl extends ServiceImpl<WholesaleMapper, Wholesale> implements WholesaleService {
|
||||
|
||||
@Autowired
|
||||
private Cache<List<Wholesale>> cache;
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "#goodsId")
|
||||
public List<Wholesale> findByGoodsId(String goodsId) {
|
||||
LambdaQueryWrapper<Wholesale> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Wholesale::getGoodsId, goodsId);
|
||||
return this.list(queryWrapper).stream().sorted(Comparator.comparing(Wholesale::getNum)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(key = "#goodsId")
|
||||
public Boolean removeByGoodsId(String goodsId) {
|
||||
LambdaQueryWrapper<Wholesale> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Wholesale::getGoodsId, goodsId);
|
||||
return this.remove(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wholesale match(String goodsId, Integer num) {
|
||||
List<Wholesale> wholesaleList = cache.get("{wholesale}_" + goodsId);
|
||||
if (wholesaleList == null) {
|
||||
wholesaleList = this.findByGoodsId(goodsId);
|
||||
cache.put("{wholesale}_" + goodsId, wholesaleList);
|
||||
}
|
||||
List<Wholesale> matchList = wholesaleList.stream()
|
||||
.filter(wholesale -> wholesale.getNum() <= num)
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(matchList)) {
|
||||
return matchList.get(matchList.size() - 1);
|
||||
} else if (CollUtil.isNotEmpty(wholesaleList) && CollUtil.isEmpty(matchList)) {
|
||||
return wholesaleList.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package cn.lili.modules.goods.sku;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.lili.modules.goods.entity.dos.Goods;
|
||||
import cn.lili.modules.goods.entity.dos.GoodsSku;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2022/5/20
|
||||
**/
|
||||
@Component
|
||||
public class GoodsSkuBuilder {
|
||||
|
||||
|
||||
private static final String IMAGES_KEY = "images";
|
||||
|
||||
|
||||
/**
|
||||
* 构建商品sku
|
||||
*
|
||||
* @param goods 商品
|
||||
* @param skuInfo sku信息列表
|
||||
* @param goodsOperationDTO 商品操作信息(如需处理额外信息传递,不需可传空)
|
||||
* @return 商品sku
|
||||
*/
|
||||
public static GoodsSku build(Goods goods, Map<String, Object> skuInfo, GoodsOperationDTO goodsOperationDTO) {
|
||||
GoodsSku goodsSku = new GoodsSku(goods);
|
||||
builderSingle(goodsSku, skuInfo, goodsOperationDTO);
|
||||
return goodsSku;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量构建商品sku
|
||||
*
|
||||
* @param goods 商品
|
||||
* @param goodsOperationDTO 商品操作信息
|
||||
* @return 商品sku
|
||||
*/
|
||||
public static List<GoodsSku> buildBatch(Goods goods, GoodsOperationDTO goodsOperationDTO) {
|
||||
return builderBatch(goods, goodsOperationDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从已有的商品sku中构建商品sku
|
||||
*
|
||||
* @param goodsSku 原商品sku
|
||||
* @param skuInfo sku信息列表
|
||||
* @param goodsOperationDTO 商品操作信息(如需处理额外信息传递,不需可传空)
|
||||
* @return 商品sku
|
||||
*/
|
||||
public static GoodsSku build(GoodsSku goodsSku, Map<String, Object> skuInfo, GoodsOperationDTO goodsOperationDTO) {
|
||||
builderSingle(goodsSku, skuInfo, goodsOperationDTO);
|
||||
return goodsSku;
|
||||
}
|
||||
|
||||
private static void builderSingle(GoodsSku goodsSku, Map<String, Object> skuInfo, GoodsOperationDTO goodsOperationDTO) {
|
||||
Assert.notNull(goodsSku, "goodsSku不能为空");
|
||||
Assert.notEmpty(skuInfo, "skuInfo不能为空");
|
||||
//规格简短信息
|
||||
StringBuilder simpleSpecs = new StringBuilder();
|
||||
//商品名称
|
||||
StringBuilder goodsName = new StringBuilder(goodsOperationDTO.getGoodsName());
|
||||
//规格值
|
||||
Map<String, Object> specMap = new HashMap<>(16);
|
||||
|
||||
// 原始规格项
|
||||
String[] ignoreOriginKeys = {"id", "sn", "cost", "price", "quantity", "weight", IMAGES_KEY};
|
||||
//获取规格信息
|
||||
for (Map.Entry<String, Object> spec : skuInfo.entrySet()) {
|
||||
//保存新增规格信息
|
||||
if (!CollUtil.contains(Arrays.asList(ignoreOriginKeys), spec.getKey()) && spec.getValue() != null) {
|
||||
specMap.put(spec.getKey(), spec.getValue());
|
||||
//设置商品名称
|
||||
goodsName.append(" ").append(spec.getValue());
|
||||
//规格简短信息
|
||||
simpleSpecs.append(" ").append(spec.getValue());
|
||||
}
|
||||
}
|
||||
//设置规格信息
|
||||
goodsSku.setGoodsName(goodsName.toString());
|
||||
|
||||
|
||||
//规格信息
|
||||
goodsSku.setId(Convert.toStr(skuInfo.get("id"), ""));
|
||||
goodsSku.setSn(Convert.toStr(skuInfo.get("sn")));
|
||||
goodsSku.setWeight(Convert.toDouble(skuInfo.get("weight"), 0D));
|
||||
goodsSku.setPrice(Convert.toDouble(skuInfo.get("price"), 0D));
|
||||
goodsSku.setCost(Convert.toDouble(skuInfo.get("cost"), 0D));
|
||||
goodsSku.setQuantity(Convert.toInt(skuInfo.get("quantity"), 0));
|
||||
goodsSku.setSpecs(JSONUtil.toJsonStr(specMap));
|
||||
goodsSku.setSimpleSpecs(simpleSpecs.toString());
|
||||
}
|
||||
|
||||
private static List<GoodsSku> builderBatch(Goods goods, GoodsOperationDTO goodsOperationDTO) {
|
||||
Assert.notEmpty(goodsOperationDTO.getSkuList(), "goodsOperationDTO.getSkuList()不能为空");
|
||||
Assert.notNull(goods, "goods不能为空");
|
||||
List<GoodsSku> goodsSkus = new ArrayList<>();
|
||||
for (Map<String, Object> skuInfo : goodsOperationDTO.getSkuList()) {
|
||||
GoodsSku goodsSku = new GoodsSku(goods);
|
||||
builderSingle(goodsSku, skuInfo, goodsOperationDTO);
|
||||
goodsSkus.add(goodsSku);
|
||||
}
|
||||
return goodsSkus;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.lili.modules.goods.sku.render;
|
||||
|
||||
import cn.lili.modules.goods.entity.dos.GoodsSku;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 根据商品销售模型渲染商品sku
|
||||
*
|
||||
* @author paulG
|
||||
* @since 2022/5/20
|
||||
**/
|
||||
public interface SalesModelRender {
|
||||
|
||||
|
||||
String getSalesMode();
|
||||
|
||||
void renderSingle(GoodsSku goodsSku, GoodsOperationDTO goodsOperationDTO);
|
||||
|
||||
void renderBatch(List<GoodsSku> goodsSkus, GoodsOperationDTO goodsOperationDTO);
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package cn.lili.modules.goods.sku.render.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.modules.goods.entity.dos.GoodsSku;
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
|
||||
import cn.lili.modules.goods.entity.dto.WholesaleDTO;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum;
|
||||
import cn.lili.modules.goods.service.WholesaleService;
|
||||
import cn.lili.modules.goods.sku.render.SalesModelRender;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2022/5/24
|
||||
**/
|
||||
@Component
|
||||
public class WholesaleSaleModelRenderImpl implements SalesModelRender {
|
||||
/**
|
||||
* 批发商品
|
||||
*/
|
||||
@Autowired
|
||||
private WholesaleService wholesaleService;
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void renderSingle(GoodsSku goodsSku, GoodsOperationDTO goodsOperationDTO) {
|
||||
Assert.notEmpty(goodsOperationDTO.getWholesaleList(), "批发规则不能为空");
|
||||
this.checkWholesaleList(goodsOperationDTO.getWholesaleList());
|
||||
List<Wholesale> collect = goodsOperationDTO.getWholesaleList().stream().sorted(Comparator.comparing(Wholesale::getPrice)).collect(Collectors.toList());
|
||||
wholesaleService.removeByGoodsId(goodsOperationDTO.getGoodsId());
|
||||
wholesaleService.saveOrUpdateBatch(collect);
|
||||
goodsSku.setPrice(collect.get(0).getPrice());
|
||||
goodsSku.setCost(collect.get(0).getPrice());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void renderBatch(List<GoodsSku> goodsSkus, GoodsOperationDTO goodsOperationDTO) {
|
||||
Assert.notEmpty(goodsOperationDTO.getWholesaleList(), "批发规则不能为空");
|
||||
this.checkWholesaleList(goodsOperationDTO.getWholesaleList());
|
||||
List<Wholesale> collect = goodsOperationDTO.getWholesaleList().stream().sorted(Comparator.comparing(Wholesale::getPrice)).collect(Collectors.toList());
|
||||
for (GoodsSku skus : goodsSkus) {
|
||||
skus.setPrice(collect.get(0).getPrice());
|
||||
skus.setCost(collect.get(0).getPrice());
|
||||
}
|
||||
wholesaleService.removeByGoodsId(goodsOperationDTO.getGoodsId());
|
||||
wholesaleService.saveOrUpdateBatch(collect);
|
||||
}
|
||||
|
||||
private void checkWholesaleList(List<WholesaleDTO> wholesaleList) {
|
||||
if (CollUtil.isEmpty(wholesaleList)) {
|
||||
throw new ServiceException(ResultCode.MUST_HAVE_SALES_MODEL);
|
||||
}
|
||||
for (WholesaleDTO wholesaleDTO : wholesaleList) {
|
||||
if (wholesaleDTO.getPrice() == null || wholesaleDTO.getPrice() <= 0 || wholesaleDTO.getNum() == null || wholesaleDTO.getNum() <= 0) {
|
||||
throw new ServiceException(ResultCode.HAVE_INVALID_SALES_MODEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSalesMode() {
|
||||
return GoodsSalesModeEnum.WHOLESALE.name();
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package cn.lili.modules.order.cart.render.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@ -7,10 +8,14 @@ import cn.lili.common.enums.PromotionTypeEnum;
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.utils.CurrencyUtil;
|
||||
import cn.lili.modules.goods.entity.dos.GoodsSku;
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
|
||||
import cn.lili.modules.goods.service.GoodsSkuService;
|
||||
import cn.lili.modules.goods.service.WholesaleService;
|
||||
import cn.lili.modules.member.entity.dos.Member;
|
||||
import cn.lili.modules.member.service.MemberService;
|
||||
import cn.lili.modules.order.cart.entity.dto.TradeDTO;
|
||||
@ -55,6 +60,9 @@ public class CheckDataRender implements CartRenderStep {
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
|
||||
@Autowired
|
||||
private WholesaleService wholesaleService;
|
||||
|
||||
|
||||
@Override
|
||||
public RenderStepEnums step() {
|
||||
@ -69,6 +77,7 @@ public class CheckDataRender implements CartRenderStep {
|
||||
//校验商品有效性
|
||||
checkData(tradeDTO);
|
||||
|
||||
preSaleModel(tradeDTO);
|
||||
//店铺分组数据初始化
|
||||
groupStore(tradeDTO);
|
||||
|
||||
@ -212,4 +221,29 @@ public class CheckDataRender implements CartRenderStep {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 商品销售模式特殊处理
|
||||
*
|
||||
* @param tradeDTO 交易信息
|
||||
*/
|
||||
private void preSaleModel(TradeDTO tradeDTO) {
|
||||
// 寻找同goods下销售模式为批发的商品
|
||||
Map<String, List<CartSkuVO>> goodsGroup = tradeDTO.getSkuList().stream().filter(i -> i.getGoodsSku().getSalesModel().equals(GoodsSalesModeEnum.WHOLESALE.name())).collect(Collectors.groupingBy(i -> i.getGoodsSku().getGoodsId()));
|
||||
if (CollUtil.isNotEmpty(goodsGroup)) {
|
||||
goodsGroup.forEach((k, v) -> {
|
||||
// 获取购买总数
|
||||
int sum = v.stream().mapToInt(CartSkuVO::getNum).sum();
|
||||
// 匹配符合的批发规则
|
||||
Wholesale match = wholesaleService.match(k, sum);
|
||||
if (match != null) {
|
||||
v.forEach(i -> {
|
||||
// 将符合规则的商品设置批发价格
|
||||
i.setPurchasePrice(match.getPrice());
|
||||
i.setSubTotal(CurrencyUtil.mul(i.getPurchasePrice(), i.getNum()));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.lili.modules.order.cart.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@ -11,11 +12,14 @@ import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.utils.CurrencyUtil;
|
||||
import cn.lili.modules.goods.entity.dos.GoodsSku;
|
||||
import cn.lili.modules.goods.entity.dos.Wholesale;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum;
|
||||
import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
|
||||
import cn.lili.modules.goods.entity.vos.GoodsVO;
|
||||
import cn.lili.modules.goods.service.GoodsService;
|
||||
import cn.lili.modules.goods.service.GoodsSkuService;
|
||||
import cn.lili.modules.goods.service.WholesaleService;
|
||||
import cn.lili.modules.member.entity.dos.Member;
|
||||
import cn.lili.modules.member.entity.dos.MemberAddress;
|
||||
import cn.lili.modules.member.service.MemberAddressService;
|
||||
@ -124,6 +128,9 @@ public class CartServiceImpl implements CartService {
|
||||
@Autowired
|
||||
private PromotionGoodsService promotionGoodsService;
|
||||
|
||||
@Autowired
|
||||
private WholesaleService wholesaleService;
|
||||
|
||||
@Override
|
||||
public void add(String skuId, Integer num, String cartType, Boolean cover) {
|
||||
AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser());
|
||||
@ -193,6 +200,7 @@ public class CartServiceImpl implements CartService {
|
||||
cartSkuVOS.add(cartSkuVO);
|
||||
}
|
||||
|
||||
this.checkGoodsSaleModel(dataSku, tradeDTO.getSkuList());
|
||||
tradeDTO.setCartTypeEnum(cartTypeEnum);
|
||||
//如购物车发生更改,则重置优惠券
|
||||
tradeDTO.setStoreCoupons(null);
|
||||
@ -554,12 +562,9 @@ public class CartServiceImpl implements CartService {
|
||||
goodsIndex = goodsIndexService.getResetEsGoodsIndex(dataSku, goodsVO.getGoodsParamsDTOList());
|
||||
}
|
||||
if (goodsIndex.getPromotionMap() != null && !goodsIndex.getPromotionMap().isEmpty()) {
|
||||
if (goodsIndex.getPromotionMap().keySet().stream().anyMatch(i -> i.contains(PromotionTypeEnum.SECKILL.name())) ||
|
||||
(goodsIndex.getPromotionMap().keySet().stream().anyMatch(i -> i.contains(PromotionTypeEnum.PINTUAN.name()))
|
||||
&& CartTypeEnum.PINTUAN.name().equals(cartType))) {
|
||||
if (goodsIndex.getPromotionMap().keySet().stream().anyMatch(i -> i.contains(PromotionTypeEnum.SECKILL.name())) || (goodsIndex.getPromotionMap().keySet().stream().anyMatch(i -> i.contains(PromotionTypeEnum.PINTUAN.name())) && CartTypeEnum.PINTUAN.name().equals(cartType))) {
|
||||
|
||||
Optional<Map.Entry<String, Object>> containsPromotion = goodsIndex.getPromotionMap().entrySet().stream().filter(i ->
|
||||
i.getKey().contains(PromotionTypeEnum.SECKILL.name()) || i.getKey().contains(PromotionTypeEnum.PINTUAN.name())).findFirst();
|
||||
Optional<Map.Entry<String, Object>> containsPromotion = goodsIndex.getPromotionMap().entrySet().stream().filter(i -> i.getKey().contains(PromotionTypeEnum.SECKILL.name()) || i.getKey().contains(PromotionTypeEnum.PINTUAN.name())).findFirst();
|
||||
if (containsPromotion.isPresent()) {
|
||||
JSONObject promotionsObj = JSONUtil.parseObj(containsPromotion.get().getValue());
|
||||
PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams();
|
||||
@ -709,6 +714,24 @@ public class CartServiceImpl implements CartService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void checkGoodsSaleModel(GoodsSku dataSku, List<CartSkuVO> cartSkuVOS) {
|
||||
if (dataSku.getSalesModel().equals(GoodsSalesModeEnum.WHOLESALE.name())) {
|
||||
int numSum = 0;
|
||||
List<CartSkuVO> sameGoodsIdSkuList = cartSkuVOS.stream().filter(i -> i.getGoodsSku().getGoodsId().equals(dataSku.getGoodsId())).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(sameGoodsIdSkuList)) {
|
||||
numSum += sameGoodsIdSkuList.stream().mapToInt(CartSkuVO::getNum).sum();
|
||||
}
|
||||
Wholesale match = wholesaleService.match(dataSku.getGoodsId(), numSum);
|
||||
if (match != null) {
|
||||
sameGoodsIdSkuList.forEach(i -> {
|
||||
i.setPurchasePrice(match.getPrice());
|
||||
i.setSubTotal(CurrencyUtil.mul(i.getPurchasePrice(), i.getNum()));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验拼团信息
|
||||
*
|
||||
|
@ -215,6 +215,7 @@ public class EsGoodsIndex implements Serializable {
|
||||
|
||||
/**
|
||||
* 销售模式
|
||||
* @see cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum
|
||||
*/
|
||||
@Field(type = FieldType.Text)
|
||||
@ApiModelProperty("销售模式")
|
||||
|
Loading…
x
Reference in New Issue
Block a user