diff --git a/mobile-web/src/config/request.js b/mobile-web/src/config/request.js index f429d5368..e4e5bcbf5 100644 --- a/mobile-web/src/config/request.js +++ b/mobile-web/src/config/request.js @@ -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' } diff --git a/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/OrderService.java b/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/OrderService.java index 45c95cd81..5ceea1df0 100644 --- a/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/OrderService.java +++ b/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/OrderService.java @@ -87,7 +87,7 @@ public interface OrderService { * @param orderDelivery * @return */ - CommonResult orderDelivery(OrderDeliveryDTO orderDelivery); + CommonResult orderDelivery(OrderDeliveryDTO orderDelivery); /** * 更新订单 - 备注 @@ -133,14 +133,6 @@ public interface OrderService { */ CommonResult deleteOrder(Integer id); - /** - * 监听支付动作 - * - * mq 更新 payStatus - */ - @Deprecated - CommonResult listenerPayment(); - /** * 更新订单支付成功 * diff --git a/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/bo/CalcSkuPriceBO.java b/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/bo/CalcSkuPriceBO.java index ed835ff70..7d3613044 100644 --- a/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/bo/CalcSkuPriceBO.java +++ b/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/bo/CalcSkuPriceBO.java @@ -16,7 +16,7 @@ public class CalcSkuPriceBO { */ private PromotionActivityBO fullPrivilege; /** - * 电视和折扣促销活动 + * 限时折扣促销活动 */ private PromotionActivityBO timeLimitedDiscount; /** diff --git a/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/dto/OrderDeliveryDTO.java b/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/dto/OrderDeliveryDTO.java index 8cb8f634e..589d1ac86 100644 --- a/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/dto/OrderDeliveryDTO.java +++ b/order/order-service-api/src/main/java/cn/iocoder/mall/order/api/dto/OrderDeliveryDTO.java @@ -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 orderItemIds; + } diff --git a/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/dataobject/OrderLogisticsDetailDO.java b/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/dataobject/OrderLogisticsDetailDO.java index 51e83cd0b..325e5f63b 100644 --- a/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/dataobject/OrderLogisticsDetailDO.java +++ b/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/dataobject/OrderLogisticsDetailDO.java @@ -34,4 +34,5 @@ public class OrderLogisticsDetailDO extends DeletableDO { * 物流信息 */ private String logisticsInformation; + } diff --git a/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/OrderServiceImpl.java b/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/OrderServiceImpl.java index 89e6594f0..bd0a8e9dc 100644 --- a/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/OrderServiceImpl.java +++ b/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/OrderServiceImpl.java @@ -198,8 +198,7 @@ public class OrderServiceImpl implements OrderService { List orderItemDOList = OrderItemConvert.INSTANCE.convert(orderItemDTOList); // 获取商品信息 - Set skuIds = orderItemDOList.stream() - .map(orderItemDO -> orderItemDO.getSkuId()).collect(Collectors.toSet()); + Set skuIds = orderItemDOList.stream().map(OrderItemDO::getSkuId).collect(Collectors.toSet()); CommonResult> 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 userAddressResult - = userAddressService.getAddress(userId, orderCreateDTO.getUserAddressId()); - + CommonResult 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 orderItemIds = orderDelivery.getOrderItemIds(); // 获取所有订单 items - List allOrderItems = orderItemMapper - .selectByDeletedAndOrderId(orderDelivery.getOrderId(), DeletedStatusEnum.DELETED_NO.getValue()); + List allOrderItems = orderItemMapper.selectByDeletedAndOrderId(orderDelivery.getOrderId(), DeletedStatusEnum.DELETED_NO.getValue()); // 当前需要发货订单,检查 id 和 status List 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"; } diff --git a/pom.xml b/pom.xml index 4faca0c36..2a0412533 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.3.RELEASE + 2.1.4.RELEASE @@ -22,11 +22,12 @@ ops pay promotion + search pom - 2.1.3.RELEASE + 2.1.4.RELEASE 2.6.5 5.1.47 0.2.1.RELEASE diff --git a/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/ProductSpuService.java b/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/ProductSpuService.java index 30b4a70ff..b2968d8dc 100644 --- a/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/ProductSpuService.java +++ b/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/ProductSpuService.java @@ -13,6 +13,15 @@ public interface ProductSpuService { CommonResult getProductSpuDetail(Integer id); + /** + * 增量获得商品列表,按照 lastId 递增获得 + * + * @param lastId 最后查询的编号 + * @param limit 大小 + * @return 商品列表 + */ + CommonResult> getProductSpuDetailListForSync(Integer lastId, Integer limit); + CommonResult getProductSpuPage(ProductSpuPageDTO productSpuPageDTO); CommonResult> getProductSpuList(Collection ids); diff --git a/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/bo/ProductSpuDetailBO.java b/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/bo/ProductSpuDetailBO.java index b35568797..ad3d7000b 100644 --- a/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/bo/ProductSpuDetailBO.java +++ b/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/bo/ProductSpuDetailBO.java @@ -35,6 +35,10 @@ public class ProductSpuDetailBO implements Serializable { * 分类编号 */ private Integer cid; + /** + * 分类名 + */ + private String categoryName; /** * 商品主图地址 * diff --git a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/convert/ProductSpuConvert.java b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/convert/ProductSpuConvert.java index a869fd877..450c2ddc8 100644 --- a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/convert/ProductSpuConvert.java +++ b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/convert/ProductSpuConvert.java @@ -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 skus, List productAttrDetailBOs) { + default ProductSpuDetailBO convert2(ProductSpuDO spu, List skus, List 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 attrs = StringUtil.split(sku.getAttrs(), ","); attrs.forEach(attr -> skuDetail.getAttrs().add(productAttrDetailBOMap.get(Integer.valueOf(attr)))); }); + // 设置分类名 + spuDetail.setCategoryName(category.getName()); // 返回 return spuDetail; } diff --git a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/dao/ProductSpuMapper.java b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/dao/ProductSpuMapper.java index aab399241..71294dd20 100644 --- a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/dao/ProductSpuMapper.java +++ b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/dao/ProductSpuMapper.java @@ -14,6 +14,16 @@ public interface ProductSpuMapper { List selectByIds(@Param("ids") Collection ids); + /** + * 获得大于 id 的商品编号数组 + * + * @param id 商品编号 + * @param limit 数量 + * @return 商品编号数组 + */ + List 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); -} \ No newline at end of file +} diff --git a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/service/ProductCategoryServiceImpl.java b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/service/ProductCategoryServiceImpl.java index d18dfa9a1..2e22a47d8 100644 --- a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/service/ProductCategoryServiceImpl.java +++ b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/service/ProductCategoryServiceImpl.java @@ -123,6 +123,10 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { return CommonResult.success(true); } + public ProductCategoryDO getProductCategory(Integer productCategoryId) { + return productCategoryMapper.selectById(productCategoryId); + } + public CommonResult validProductCategory(Integer productCategoryId) { // 校验分类是否存在 ProductCategoryDO productCategory = productCategoryMapper.selectById(productCategoryId); @@ -142,4 +146,4 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { || ProductCategoryConstants.STATUS_DISABLE.equals(status); } -} \ No newline at end of file +} diff --git a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/service/ProductSpuServiceImpl.java b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/service/ProductSpuServiceImpl.java index c4bde3509..0ef074be7 100644 --- a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/service/ProductSpuServiceImpl.java +++ b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/service/ProductSpuServiceImpl.java @@ -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 skus = productSkuMapper.selectListBySpuIdAndStatus(id, ProductSpuConstants.SKU_STATUS_ENABLE); // 获得规格 @@ -62,7 +66,20 @@ public class ProductSpuServiceImpl implements ProductSpuService { CommonResult> 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> getProductSpuDetailListForSync(Integer lastId, Integer limit) { + // TODO 芋艿,这里目前是一个一个进行计算,后续需要优化下 + // 查询下一批商品编号集合 + List spuIds = productSpuMapper.selectIdListByIdGt(lastId, limit); + if (spuIds.isEmpty()) { + return CommonResult.success(Collections.emptyList()); + } + // 查询每个商品明细 + List 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") diff --git a/product/product-service-impl/src/main/resources/mapper/ProductSpuMapper.xml b/product/product-service-impl/src/main/resources/mapper/ProductSpuMapper.xml index 7b0d3724d..8697c130f 100644 --- a/product/product-service-impl/src/main/resources/mapper/ProductSpuMapper.xml +++ b/product/product-service-impl/src/main/resources/mapper/ProductSpuMapper.xml @@ -27,6 +27,20 @@ AND deleted = 0 + + INSERT INTO product_spu ( name, sell_point, description, cid, pic_urls, @@ -114,4 +128,4 @@ - \ No newline at end of file + diff --git a/promotion/promotion-service-api/pom.xml b/promotion/promotion-service-api/pom.xml index 2aae54544..cfbce1019 100644 --- a/promotion/promotion-service-api/pom.xml +++ b/promotion/promotion-service-api/pom.xml @@ -47,5 +47,4 @@ - diff --git a/search/pom.xml b/search/pom.xml new file mode 100644 index 000000000..fd9f3b1e2 --- /dev/null +++ b/search/pom.xml @@ -0,0 +1,21 @@ + + + + mall-parent + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + search + pom + + search-application + search-service-api + search-service-impl + + + + diff --git a/search/search-application/pom.xml b/search/search-application/pom.xml new file mode 100644 index 000000000..519c43bef --- /dev/null +++ b/search/search-application/pom.xml @@ -0,0 +1,15 @@ + + + + search + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + search-application + + + diff --git a/search/search-service-api/pom.xml b/search/search-service-api/pom.xml new file mode 100644 index 000000000..7bfb59ff3 --- /dev/null +++ b/search/search-service-api/pom.xml @@ -0,0 +1,50 @@ + + + + search + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + search-service-api + + + + cn.iocoder.mall + common-framework + 1.0-SNAPSHOT + + + + javax.validation + validation-api + + + + org.mapstruct + mapstruct + + + org.mapstruct + mapstruct-jdk8 + + + org.projectlombok + lombok + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/ProductSearchService.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/ProductSearchService.java new file mode 100644 index 000000000..3d709801c --- /dev/null +++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/ProductSearchService.java @@ -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 rebuild(); + + CommonResult searchPage(ProductSearchPageDTO searchPageDTO); + +} diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductSearchPageDTO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductSearchPageDTO.java new file mode 100644 index 000000000..09d95c0af --- /dev/null +++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductSearchPageDTO.java @@ -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 sorts; + +} diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/SortFieldDTO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/SortFieldDTO.java new file mode 100644 index 000000000..d093be9f9 --- /dev/null +++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/SortFieldDTO.java @@ -0,0 +1,17 @@ +package cn.iocoder.mall.search.api.dto; + +/** + * 排序字段 DTO + */ +public class SortFieldDTO { + + /** + * 字段 + */ + private String field; + /** + * 排序 + */ + private String order; + +} diff --git a/search/search-service-impl/pom.xml b/search/search-service-impl/pom.xml new file mode 100644 index 000000000..6cc7e612a --- /dev/null +++ b/search/search-service-impl/pom.xml @@ -0,0 +1,64 @@ + + + + search + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + search-service-impl + + + + cn.iocoder.mall + search-service-api + 1.0-SNAPSHOT + + + cn.iocoder.mall + product-service-api + 1.0-SNAPSHOT + + + cn.iocoder.mall + order-service-api + 1.0-SNAPSHOT + + + + com.alibaba + dubbo + + + + com.google.guava + guava + + + + org.springframework.boot + spring-boot-starter-data-elasticsearch + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/constant/FieldAnalyzer.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/constant/FieldAnalyzer.java new file mode 100644 index 000000000..5b5315769 --- /dev/null +++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/constant/FieldAnalyzer.java @@ -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"; + +} diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/dao/ProductRepository.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/dao/ProductRepository.java new file mode 100644 index 000000000..931c7b55d --- /dev/null +++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/dao/ProductRepository.java @@ -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 { + + @Deprecated + ESProductDO findByName(String name); + +} diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/dataobject/ESProductDO.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/dataobject/ESProductDO.java new file mode 100644 index 000000000..bd06b0abf --- /dev/null +++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/dataobject/ESProductDO.java @@ -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 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; + +} diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java new file mode 100644 index 000000000..3935e1bef --- /dev/null +++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java @@ -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 rebuild() { + // TODO 芋艿,因为目前商品比较少,所以写的很粗暴。等未来重构 + Integer lastId = null; + int rebuildCounts = 0; + while (true) { + CommonResult> result = productSpuService.getProductSpuDetailListForSync(lastId, REBUILD_FETCH_PER_SIZE); + Assert.isTrue(result.isError(), "获得商品列表必然成功"); + List spus = result.getData(); + rebuildCounts += spus.size(); + // 存储到 ES 中 + List products = spus.stream().map(new Function() { + @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 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; + } + +} diff --git a/search/search-service-impl/src/main/resources/application.yaml b/search/search-service-impl/src/main/resources/application.yaml new file mode 100644 index 000000000..56c6fc1d8 --- /dev/null +++ b/search/search-service-impl/src/main/resources/application.yaml @@ -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 diff --git a/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/Application.java b/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/Application.java new file mode 100644 index 000000000..fe2e3da2a --- /dev/null +++ b/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/Application.java @@ -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 { +} diff --git a/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/dao/ProductRepositoryTest.java b/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/dao/ProductRepositoryTest.java new file mode 100644 index 000000000..73ce7be65 --- /dev/null +++ b/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/dao/ProductRepositoryTest.java @@ -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); + } + +}