后端:尝试引入 ES 服务
parent
6fdec47430
commit
7fcb9da38e
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
/**
|
||||
* 更新订单支付成功
|
||||
*
|
||||
|
|
|
@ -16,7 +16,7 @@ public class CalcSkuPriceBO {
|
|||
*/
|
||||
private PromotionActivityBO fullPrivilege;
|
||||
/**
|
||||
* 电视和折扣促销活动
|
||||
* 限时折扣促销活动
|
||||
*/
|
||||
private PromotionActivityBO timeLimitedDiscount;
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -34,4 +34,5 @@ public class OrderLogisticsDetailDO extends DeletableDO {
|
|||
* 物流信息
|
||||
*/
|
||||
private String logisticsInformation;
|
||||
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
5
pom.xml
5
pom.xml
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -35,6 +35,10 @@ public class ProductSpuDetailBO implements Serializable {
|
|||
* 分类编号
|
||||
*/
|
||||
private Integer cid;
|
||||
/**
|
||||
* 分类名
|
||||
*/
|
||||
private String categoryName;
|
||||
/**
|
||||
* 商品主图地址
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -29,4 +39,4 @@ public interface ProductSpuMapper {
|
|||
@Param("cid") Integer cid,
|
||||
@Param("visible") Boolean visible);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -142,4 +146,4 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
|
|||
|| ProductCategoryConstants.STATUS_DISABLE.equals(status);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
@ -114,4 +128,4 @@
|
|||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
|
|
@ -47,5 +47,4 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package cn.iocoder.mall.search.api.dto;
|
||||
|
||||
/**
|
||||
* 排序字段 DTO
|
||||
*/
|
||||
public class SortFieldDTO {
|
||||
|
||||
/**
|
||||
* 字段
|
||||
*/
|
||||
private String field;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private String order;
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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";
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 {
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue