后端:尝试引入 ES 服务

This commit is contained in:
YunaiV 2019-04-24 00:32:09 +08:00
parent 6fdec47430
commit 7fcb9da38e
29 changed files with 577 additions and 44 deletions

View File

@ -100,7 +100,7 @@ const serviceRouter = function(requestUrl) {
const service = axios.create({
// baseURL: baseUrl, // api 的 base_url
timeout: 5000, // request timeout
timeout: 30000, // request timeout
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}

View File

@ -87,7 +87,7 @@ public interface OrderService {
* @param orderDelivery
* @return
*/
CommonResult orderDelivery(OrderDeliveryDTO orderDelivery);
CommonResult<OrderRecipientBO> orderDelivery(OrderDeliveryDTO orderDelivery);
/**
* 更新订单 - 备注
@ -133,14 +133,6 @@ public interface OrderService {
*/
CommonResult deleteOrder(Integer id);
/**
* 监听支付动作
*
* mq 更新 payStatus
*/
@Deprecated
CommonResult listenerPayment();
/**
* 更新订单支付成功
*

View File

@ -16,7 +16,7 @@ public class CalcSkuPriceBO {
*/
private PromotionActivityBO fullPrivilege;
/**
* 电视和折扣促销活动
* 限时折扣促销活动
*/
private PromotionActivityBO timeLimitedDiscount;
/**

View File

@ -20,12 +20,13 @@ public class OrderDeliveryDTO implements Serializable {
* 订单id
*/
private Integer orderId;
// TODO 芋艿物流方式会存在无需物流的情况
/**
* 物流 (字典)
* 物流公司 (字典)
*/
private Integer logistics;
/**
* 物流编号
* 物流编号
*/
private String logisticsNo;
@ -36,4 +37,5 @@ public class OrderDeliveryDTO implements Serializable {
* 订单 orderItemId
*/
private List<Integer> orderItemIds;
}

View File

@ -34,4 +34,5 @@ public class OrderLogisticsDetailDO extends DeletableDO {
* 物流信息
*/
private String logisticsInformation;
}

View File

@ -198,8 +198,7 @@ public class OrderServiceImpl implements OrderService {
List<OrderItemDO> orderItemDOList = OrderItemConvert.INSTANCE.convert(orderItemDTOList);
// 获取商品信息
Set<Integer> skuIds = orderItemDOList.stream()
.map(orderItemDO -> orderItemDO.getSkuId()).collect(Collectors.toSet());
Set<Integer> skuIds = orderItemDOList.stream().map(OrderItemDO::getSkuId).collect(Collectors.toSet());
CommonResult<List<ProductSkuDetailBO>> productResult = productSpuService.getProductSkuDetailList(skuIds);
// 校验商品信息
@ -273,16 +272,13 @@ public class OrderServiceImpl implements OrderService {
.setStatus(OrderStatusEnum.WAITING_PAYMENT.getValue())
.setHasReturnExchange(OrderHasReturnExchangeEnum.NO.getValue())
.setRemark(Optional.ofNullable(orderCreateDTO.getRemark()).orElse(""));
orderDO.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
orderDO.setCreateTime(new Date());
orderDO.setUpdateTime(null);
orderMapper.insert(orderDO);
// 收件人信息
CommonResult<UserAddressBO> userAddressResult
= userAddressService.getAddress(userId, orderCreateDTO.getUserAddressId());
CommonResult<UserAddressBO> userAddressResult = userAddressService.getAddress(userId, orderCreateDTO.getUserAddressId());
if (userAddressResult.isError()) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_GET_USER_ADDRESS_FAIL.getCode());
}
@ -293,7 +289,6 @@ public class OrderServiceImpl implements OrderService {
.setType(OrderRecipientTypeEnum.EXPRESS.getValue())
.setCreateTime(new Date())
.setUpdateTime(null);
orderRecipientMapper.insert(orderRecipientDO);
// order item
@ -312,7 +307,6 @@ public class OrderServiceImpl implements OrderService {
.setCreateTime(new Date())
.setUpdateTime(null);
});
// 一次性插入
orderItemMapper.insert(orderItemDOList);
@ -367,7 +361,7 @@ public class OrderServiceImpl implements OrderService {
);
}
@Override
@Override // TODO 芋艿需要确认下这个方法的用途因为涉及修改价格和数量
public CommonResult updateOrderItem(OrderItemUpdateDTO orderUpdateDTO) {
OrderItemDO orderItemDO = OrderItemConvert.INSTANCE.convert(orderUpdateDTO);
orderItemMapper.updateById(orderItemDO);
@ -410,7 +404,7 @@ public class OrderServiceImpl implements OrderService {
}
@Override
@Transactional
@Transactional // TODO 芋艿要校验下 userId 不然可以取消任何用户的订单列
public CommonResult cancelOrder(Integer orderId, Integer reason, String otherReason) {
// 关闭订单在用户还未付款的时候可操作
OrderDO orderDO = orderMapper.selectById(orderId);
@ -451,15 +445,13 @@ public class OrderServiceImpl implements OrderService {
List<Integer> orderItemIds = orderDelivery.getOrderItemIds();
// 获取所有订单 items
List<OrderItemDO> allOrderItems = orderItemMapper
.selectByDeletedAndOrderId(orderDelivery.getOrderId(), DeletedStatusEnum.DELETED_NO.getValue());
List<OrderItemDO> allOrderItems = orderItemMapper.selectByDeletedAndOrderId(orderDelivery.getOrderId(), DeletedStatusEnum.DELETED_NO.getValue());
// 当前需要发货订单检查 id status
List<OrderItemDO> needDeliveryOrderItems = allOrderItems.stream()
.filter(orderItemDO -> orderItemIds.contains(orderItemDO.getId())
&& OrderStatusEnum.WAIT_SHIPMENT.getValue() == orderItemDO.getStatus())
.collect(Collectors.toList());
// 发货订单检查
if (needDeliveryOrderItems.size() != orderItemIds.size()) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_DELIVERY_INCORRECT_DATA.getCode());
@ -467,14 +459,12 @@ public class OrderServiceImpl implements OrderService {
OrderRecipientDO orderRecipientDO = orderRecipientMapper.selectByOrderId(orderDelivery.getOrderId());
OrderLogisticsDO orderLogisticsDO = OrderLogisticsConvert.INSTANCE.convert(orderRecipientDO);
// 保存物流信息
orderLogisticsDO
.setLogisticsNo(orderDelivery.getLogisticsNo())
.setLogistics(orderDelivery.getLogistics())
.setCreateTime(new Date())
.setUpdateTime(null);
orderLogisticsMapper.insert(orderLogisticsDO);
// 关联订单item 物流信息
@ -490,7 +480,6 @@ public class OrderServiceImpl implements OrderService {
.filter(orderItemDO -> OrderStatusEnum.WAIT_SHIPMENT.getValue() == orderItemDO.getStatus()
&& !orderItemIds.contains(orderItemDO.getId()))
.collect(Collectors.toList());
if (unShippedOrderItems.size() <= 0) {
orderMapper.updateById(
new OrderDO()
@ -498,7 +487,7 @@ public class OrderServiceImpl implements OrderService {
.setStatus(OrderStatusEnum.ALREADY_SHIPMENT.getValue())
);
}
// 返回成功
return CommonResult.success(null);
}
@ -593,11 +582,6 @@ public class OrderServiceImpl implements OrderService {
return CommonResult.success(null);
}
@Override
public CommonResult listenerPayment() {
return null;
}
@Override
public String updatePaySuccess(String orderId, Integer payAmount) {
OrderDO order = orderMapper.selectById(Integer.valueOf(orderId));
@ -610,6 +594,7 @@ public class OrderServiceImpl implements OrderService {
if (!order.getPresentPrice().equals(payAmount)) { // 支付金额不正确
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_PAY_AMOUNT_ERROR.getCode()).getMessage();
}
// 更新 OrderDO 状态为已支付等待发货
OrderDO updateOrderObj = new OrderDO()
.setStatus(OrderStatusEnum.WAIT_SHIPMENT.getValue())
.setPayAmount(payAmount)
@ -618,6 +603,7 @@ public class OrderServiceImpl implements OrderService {
if (updateCount <= 0) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_STATUS_NOT_WAITING_PAYMENT.getCode()).getMessage();
}
// TODO 芋艿 更新 OrderItemDO
return "success";
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
@ -22,11 +22,12 @@
<module>ops</module>
<module>pay</module>
<module>promotion</module>
<module>search</module>
</modules>
<packaging>pom</packaging>
<properties>
<springboot.version>2.1.3.RELEASE</springboot.version>
<springboot.version>2.1.4.RELEASE</springboot.version>
<com.alibab.dubbo.version>2.6.5</com.alibab.dubbo.version>
<mysql-connector-java.version>5.1.47</mysql-connector-java.version>
<dubbo-spring-boot-starter.version>0.2.1.RELEASE</dubbo-spring-boot-starter.version>

View File

@ -13,6 +13,15 @@ public interface ProductSpuService {
CommonResult<ProductSpuDetailBO> getProductSpuDetail(Integer id);
/**
* 增量获得商品列表按照 lastId 递增获得
*
* @param lastId 最后查询的编号
* @param limit 大小
* @return 商品列表
*/
CommonResult<List<ProductSpuDetailBO>> getProductSpuDetailListForSync(Integer lastId, Integer limit);
CommonResult<ProductSpuPageBO> getProductSpuPage(ProductSpuPageDTO productSpuPageDTO);
CommonResult<List<ProductSpuBO>> getProductSpuList(Collection<Integer> ids);

View File

@ -35,6 +35,10 @@ public class ProductSpuDetailBO implements Serializable {
* 分类编号
*/
private Integer cid;
/**
* 分类名
*/
private String categoryName;
/**
* 商品主图地址
*

View File

@ -5,6 +5,7 @@ import cn.iocoder.mall.product.api.bo.*;
import cn.iocoder.mall.product.api.dto.ProductSkuAddOrUpdateDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuAddDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuUpdateDTO;
import cn.iocoder.mall.product.dataobject.ProductCategoryDO;
import cn.iocoder.mall.product.dataobject.ProductSkuDO;
import cn.iocoder.mall.product.dataobject.ProductSpuDO;
import org.mapstruct.Mapper;
@ -76,7 +77,8 @@ public interface ProductSpuConvert {
ProductSkuBO convert4(ProductSkuDO sku);
@Mappings({}) // TODO 芋艿后续细看下 mapstruct API 优化这块
default ProductSpuDetailBO convert2(ProductSpuDO spu, List<ProductSkuDO> skus, List<ProductAttrAndValuePairBO> productAttrDetailBOs) {
default ProductSpuDetailBO convert2(ProductSpuDO spu, List<ProductSkuDO> skus, List<ProductAttrAndValuePairBO> productAttrDetailBOs,
ProductCategoryDO category) {
// 创建并转换 ProductSpuDetailBO 对象
ProductSpuDetailBO spuDetail = this.convert2(spu).setPicUrls(StringUtil.split(spu.getPicUrls(), ","));
// 创建 ProductAttrDetailBO 的映射其中KEY ProductAttrDetailBO.attrValueId 即规格值的编号
@ -93,6 +95,8 @@ public interface ProductSpuConvert {
List<String> attrs = StringUtil.split(sku.getAttrs(), ",");
attrs.forEach(attr -> skuDetail.getAttrs().add(productAttrDetailBOMap.get(Integer.valueOf(attr))));
});
// 设置分类名
spuDetail.setCategoryName(category.getName());
// 返回
return spuDetail;
}

View File

@ -14,6 +14,16 @@ public interface ProductSpuMapper {
List<ProductSpuDO> selectByIds(@Param("ids") Collection<Integer> ids);
/**
* 获得大于 id 的商品编号数组
*
* @param id 商品编号
* @param limit 数量
* @return 商品编号数组
*/
List<Integer> selectIdListByIdGt(@Param("id") Integer id,
@Param("limit") Integer limit);
void insert(ProductSpuDO productSpuDO);
void update(ProductSpuDO productSpuDO);

View File

@ -123,6 +123,10 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
return CommonResult.success(true);
}
public ProductCategoryDO getProductCategory(Integer productCategoryId) {
return productCategoryMapper.selectById(productCategoryId);
}
public CommonResult<ProductCategoryDO> validProductCategory(Integer productCategoryId) {
// 校验分类是否存在
ProductCategoryDO productCategory = productCategoryMapper.selectById(productCategoryId);

View File

@ -22,6 +22,7 @@ import cn.iocoder.mall.product.dataobject.ProductSpuDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import java.util.*;
import java.util.stream.Collectors;
@ -54,6 +55,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
if (spu == null) {
return ServiceExceptionUtil.error(ProductErrorCodeEnum.PRODUCT_SPU_NOT_EXISTS.getCode());
}
// 获得商品分类分类
ProductCategoryDO category = productCategoryService.getProductCategory(spu.getCid());
Assert.notNull(category, String.format("分类编号(%d) 对应", spu.getCid()));
// 获得商品 sku 数组
List<ProductSkuDO> skus = productSkuMapper.selectListBySpuIdAndStatus(id, ProductSpuConstants.SKU_STATUS_ENABLE);
// 获得规格
@ -62,7 +66,20 @@ public class ProductSpuServiceImpl implements ProductSpuService {
CommonResult<List<ProductAttrAndValuePairBO>> validAttrResult = productAttrService.validProductAttrAndValue(productAttrValueIds,
false); // 读取规格时不考虑规格是否被禁用
// 返回成功
return CommonResult.success(ProductSpuConvert.INSTANCE.convert2(spu, skus, validAttrResult.getData()));
return CommonResult.success(ProductSpuConvert.INSTANCE.convert2(spu, skus, validAttrResult.getData(), category));
}
@Override
public CommonResult<List<ProductSpuDetailBO>> getProductSpuDetailListForSync(Integer lastId, Integer limit) {
// TODO 芋艿这里目前是一个一个进行计算后续需要优化下
// 查询下一批商品编号集合
List<Integer> spuIds = productSpuMapper.selectIdListByIdGt(lastId, limit);
if (spuIds.isEmpty()) {
return CommonResult.success(Collections.emptyList());
}
// 查询每个商品明细
List<ProductSpuDetailBO> spus = spuIds.stream().map(id -> getProductSpuDetail(id).getData()).collect(Collectors.toList()); // TODO 芋艿此处相当于是 N 个查询后续要优化
return CommonResult.success(spus);
}
@SuppressWarnings("Duplicates")
@ -108,7 +125,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
}
productSkuMapper.insertList(skus);
// 返回成功
return CommonResult.success(ProductSpuConvert.INSTANCE.convert2(spu, skus, validAttrResult.getData()));
return CommonResult.success(ProductSpuConvert.INSTANCE.convert2(spu, skus, validAttrResult.getData(),
validCategoryResult.getData()));
}
@SuppressWarnings("Duplicates")

View File

@ -27,6 +27,20 @@
AND deleted = 0
</select>
<select id="selectIdListByIdGt" parameterType="Integer" resultType="Integer">
SELECT
<include refid="FIELDS" />
FROM product_spu
<where>
<if test="id != null">
id >= #{id}
</if>
</where>
AND deleted = 0
ORDER BY id ASC
LIMIT #{limit}
</select>
<insert id="insert" parameterType="ProductSpuDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
INSERT INTO product_spu (
name, sell_point, description, cid, pic_urls,

View File

@ -47,5 +47,4 @@
</plugins>
</build>
</project>

21
search/pom.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mall-parent</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>search</artifactId>
<packaging>pom</packaging>
<modules>
<module>search-application</module>
<module>search-service-api</module>
<module>search-service-impl</module>
</modules>
</project>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>search</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>search-application</artifactId>
</project>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>search</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>search-service-api</artifactId>
<dependencies>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 提供给 mapstruct 使用 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,12 @@
package cn.iocoder.mall.search.api;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO;
public interface ProductSearchService {
CommonResult<Integer> rebuild();
CommonResult searchPage(ProductSearchPageDTO searchPageDTO);
}

View File

@ -0,0 +1,38 @@
package cn.iocoder.mall.search.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 商品检索分页 DTO
*/
@Data
@Accessors(chain = true)
public class ProductSearchPageDTO {
/**
* 分类编号
*/
private Integer cid;
/**
* 关键字
*/
private String keyword;
/**
* 页码
*/
private Integer pageNo;
/**
* 分页大小
*/
private Integer pageSize;
/**
* 排序字段数组
*/
private List<SortFieldDTO> sorts;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.mall.search.api.dto;
/**
* 排序字段 DTO
*/
public class SortFieldDTO {
/**
* 字段
*/
private String field;
/**
* 排序
*/
private String order;
}

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>search</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>search-service-impl</artifactId>
<dependencies>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>search-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>product-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>order-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 提供给 mapstruct 使用 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,26 @@
package cn.iocoder.mall.search.biz.constant;
/**
* ES 字段分析器的枚举类
*
* 关于 IK 分词文章 https://blog.csdn.net/xsdxs/article/details/72853288 不错
* 目前项目使用的 ES 版本是 6.7.1 可以在 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-7-1 下载
* 如果不知道怎么安装 ES 可以看 https://blog.csdn.net/chengyuqiang/article/details/78837712 简单
*/
public class FieldAnalyzer {
/**
* IK 最大化分词
*
* 会将文本做最细粒度的拆分
*/
public static final String IK_MAX_WORD = "ik_max_word";
/**
* IK 智能分词
*
* 会做最粗粒度的拆分
*/
public static final String IK_SMART = "ik_smart";
}

View File

@ -0,0 +1,13 @@
package cn.iocoder.mall.search.biz.dao;
import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends ElasticsearchRepository<ESProductDO, Integer> {
@Deprecated
ESProductDO findByName(String name);
}

View File

@ -0,0 +1,96 @@
package cn.iocoder.mall.search.biz.dataobject;
import cn.iocoder.mall.search.biz.constant.FieldAnalyzer;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.util.List;
/**
* 商品 ES DO
*/
@Document(indexName = "product", type = "spu", shards = 1, replicas = 0)
@Data
@Accessors(chain = true)
public class ESProductDO {
@Id
private Integer id;
// ========== 基本信息 =========
/**
* SPU 名字
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String name;
/**
* 卖点
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String sellPoint;
/**
* 描述
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String description;
/**
* 分类编号
*/
private Integer cid;
/**
* 分类名
*/
@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
private String categoryName;
/**
* 商品主图地数组
*/
private List<String> picUrls;
// ========== 其他信息 =========
/**
* 是否上架商品是否可见
*
* true 为已上架
* false 为已下架
*/
private Boolean visible;
/**
* 排序字段
*/
private Integer sort;
// ========== Sku 相关字段 =========
/**
* 原价格单位
*/
private Integer originalPrice;
/**
* 购买价格单位
*/
private Integer buyPrice;
/**
* 库存数量
*/
private Integer quantity;
// ========== 促销活动相关字段 =========
// 目前只促销单体商品促销目前仅限制折扣
/**
* 促销活动编号
*/
private Integer promotionActivityId;
/**
* 促销活动标题
*/
private String promotionActivityTitle;
/**
* 促销活动类型
*/
private Integer promotionActivityType;
}

View File

@ -0,0 +1,79 @@
package cn.iocoder.mall.search.biz.service;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.order.api.CartService;
import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO;
import cn.iocoder.mall.product.api.ProductSpuService;
import cn.iocoder.mall.product.api.bo.ProductSpuDetailBO;
import cn.iocoder.mall.search.api.ProductSearchService;
import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO;
import cn.iocoder.mall.search.biz.dao.ProductRepository;
import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@com.alibaba.dubbo.config.annotation.Service(validation = "true")
public class ProductSearchServiceImpl implements ProductSearchService {
private static final Integer REBUILD_FETCH_PER_SIZE = 2;
@Autowired
private ProductRepository productRepository;
@Autowired
private ProductSpuService productSpuService;
@Autowired
private CartService cartService;
@Override
public CommonResult<Integer> rebuild() {
// TODO 芋艿因为目前商品比较少所以写的很粗暴等未来重构
Integer lastId = null;
int rebuildCounts = 0;
while (true) {
CommonResult<List<ProductSpuDetailBO>> result = productSpuService.getProductSpuDetailListForSync(lastId, REBUILD_FETCH_PER_SIZE);
Assert.isTrue(result.isError(), "获得商品列表必然成功");
List<ProductSpuDetailBO> spus = result.getData();
rebuildCounts += spus.size();
// 存储到 ES
List<ESProductDO> products = spus.stream().map(new Function<ProductSpuDetailBO, ESProductDO>() {
@Override
public ESProductDO apply(ProductSpuDetailBO spu) {
return convert(spu);
}
}).collect(Collectors.toList());
// 设置新的 lastId 或者结束
if (spus.size() < REBUILD_FETCH_PER_SIZE) {
break;
} else {
lastId = spus.get(spus.size() - 1).getId();
}
}
return CommonResult.success(rebuildCounts);
}
@SuppressWarnings("OptionalGetWithoutIsPresent")
private ESProductDO convert(ProductSpuDetailBO spu) {
// 获得最小价格的 SKU 用于下面的价格计算
ProductSpuDetailBO.Sku sku = spu.getSkus().stream().min(Comparator.comparing(ProductSpuDetailBO.Sku::getPrice)).get();
// 价格计算
CommonResult<CalcSkuPriceBO> calSkuPriceResult = cartService.calcSkuPrice(sku.getId());
Assert.isTrue(calSkuPriceResult.isError(), String.format("SKU(%d) 价格计算不会出错", sku.getId()));
return new ESProductDO();
}
@Override
public CommonResult searchPage(ProductSearchPageDTO searchPageDTO) {
return null;
}
}

View File

@ -0,0 +1,19 @@
#
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.88.10:9300
repositories:
enable: true
# dubbo
dubbo:
application:
name: search-service
registry:
address: zookeeper://127.0.0.1:2181
protocol:
port: -1
name: dubbo
scan:
base-packages: cn.iocoder.mall.search.service.biz

View File

@ -0,0 +1,7 @@
package cn.iocoder.mall.search.biz;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.search"})
public class Application {
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.mall.search.biz.dao;
import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
@Test
public void testSave() {
// productRepository.deleteById(1);
ESProductDO product = new ESProductDO()
.setId(1)
.setName("你猜");
productRepository.save(product);
}
@Test
public void testFindByName() {
ESProductDO product = productRepository.findByName("锤子");
System.out.println(product);
}
}