diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DateUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DateUtil.java index 3219252f0..67f06e5ad 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DateUtil.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DateUtil.java @@ -1,5 +1,7 @@ package cn.iocoder.common.framework.util; +import org.springframework.util.Assert; + import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -116,4 +118,19 @@ public class DateUtil { calendar.set(Calendar.MILLISECOND, milliSecond); } + /** + * 判断当前时间,是否在该时间范围内 + * + * @param beginTime 开始时间 + * @param endTime 结束时间 + * @return 是否在 + */ + public static boolean isBetween(Date beginTime, Date endTime) { + Assert.notNull(beginTime, "开始时间不能为空"); + Assert.notNull(endTime, "结束时间不能为空"); + Date now = new Date(); + return beginTime.getTime() <= now.getTime() + && now.getTime() <= endTime.getTime(); + } + } diff --git a/mobile-web/src/api/search.js b/mobile-web/src/api/search.js index 34633bb75..0b00eb19c 100644 --- a/mobile-web/src/api/search.js +++ b/mobile-web/src/api/search.js @@ -14,3 +14,13 @@ export function getProductPage({cid, keyword, pageNo, pageSize, sortField, sortO } }); } + +export function getProductCondition({keyword}) { + return request({ + url: '/search-api/users/product/condition', + method: 'get', + params: { + keyword, + } + }); +} diff --git a/mobile-web/src/page/product/search.vue b/mobile-web/src/page/product/search.vue index 39bc1ddaf..5a4a6f15a 100644 --- a/mobile-web/src/page/product/search.vue +++ b/mobile-web/src/page/product/search.vue @@ -15,152 +15,160 @@
  • 价格最高
  • - -
    - - - - - -
    - 清楚选项 -
    -
    -
    - 取消 - 确认 -
    -
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + import searchtop from "../../components/search/searchtop"; -import {getProductPage} from "../../api/search"; +import {getProductCondition, getProductPage} from "../../api/search"; export default { components: { @@ -201,13 +209,16 @@ export default { sortField: undefined, sortOrder: undefined, - products:[] + products:[], // 搜索出的商品 + categories: [], // 筛选的分类 + categoryId: undefined, // 选中的分类编号 }; }, methods: { onFilterBar(value) { if (value === 0) { this.filterSort = !this.filterSort; + this.filterShow = false; } else if (value === 3) { this.filterShow = !this.filterShow; } else { @@ -252,6 +263,25 @@ export default { }); } }, + onCategoryClick(value) { + // 设置分类编号 + this.categoryId = value; + // 隐藏弹出 + this.filterShow = false; + // 根据分类,重新搜索 + let page = 1; + getProductPage({ + pageNo: page, + pageSize: this.pageSize, + keyword: this.keyword, + sortField: this.sortField, + sortOrder: this.sortOrder, + cid: this.categoryId, + }).then(data => { + this.products = []; + this.handleData(page, data); + }); + }, showProduct(product){ this.$router.push('/product/'+product.id); }, @@ -265,6 +295,7 @@ export default { this.filterShow = false; this.sortField = undefined; this.sortOrder = undefined; + this.categoryId = undefined; // 查询 let page = 1; getProductPage({ @@ -274,6 +305,7 @@ export default { }).then(data => { this.products = []; this.handleData(page, data); + this.loadSearchCondition(); }); }, onLoad() { @@ -286,6 +318,7 @@ export default { keyword: this.keyword, }).then(data => { this.handleData(page, data); + this.loadSearchCondition(); }); }, handleData(page, data) { @@ -300,6 +333,13 @@ export default { } // 标记不在加载中 this.loading = false; + }, + loadSearchCondition() { + getProductCondition({ + keyword: this.keyword, + }).then(data => { + this.categories = data.categories; + }); } }, mounted() { diff --git a/order/order-application/src/main/java/cn/iocoder/mall/order/application/controller/users/UsersOrderController.java b/order/order-application/src/main/java/cn/iocoder/mall/order/application/controller/users/UsersOrderController.java index 4d47364b1..828db1ec2 100644 --- a/order/order-application/src/main/java/cn/iocoder/mall/order/application/controller/users/UsersOrderController.java +++ b/order/order-application/src/main/java/cn/iocoder/mall/order/application/controller/users/UsersOrderController.java @@ -129,4 +129,5 @@ public class UsersOrderController { orderInfoBO.setStatusText(dictResult.getData().getDisplayName()); return commonResult; } + } 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 702508dd7..5a7f56db4 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 @@ -21,6 +21,7 @@ import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO; import cn.iocoder.mall.product.api.ProductSpuService; import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO; +import cn.iocoder.mall.promotion.api.CouponService; import cn.iocoder.mall.user.api.UserAddressService; import cn.iocoder.mall.user.api.bo.UserAddressBO; import com.alibaba.dubbo.config.annotation.Reference; @@ -62,14 +63,17 @@ public class OrderServiceImpl implements OrderService { @Autowired private OrderCancelMapper orderCancelMapper; - @Reference - private ProductSpuService productSpuService; @Autowired private CartServiceImpl cartService; - @Reference + + @Reference(validation = "true") + private ProductSpuService productSpuService; + @Reference(validation = "true") private UserAddressService userAddressService; @Reference(validation = "true") private PayTransactionService payTransactionService; + @Reference(validation = "true") + private CouponService couponService; @Override public CommonResult getOrderPage(OrderQueryDTO orderQueryDTO) { @@ -248,7 +252,12 @@ public class OrderServiceImpl implements OrderService { .setPresentTotal(priceItem.getPresentTotal()); } - // TODO 芋艿,标记优惠劵使用 + // 标记优惠劵已使用 + CommonResult useCouponCardResult = couponService.useCouponCard(userId, orderCreateDTO.getCouponCardId()); + if (useCouponCardResult.isError()) { + return CommonResult.error(useCouponCardResult); + } + // TODO 芋艿,扣除库存 // order diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/mq/PayTransactionPaySuccessMessage.java b/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/message/PayTransactionPaySuccessMessage.java similarity index 87% rename from pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/mq/PayTransactionPaySuccessMessage.java rename to pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/message/PayTransactionPaySuccessMessage.java index e83cbce9d..914f8e91b 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/mq/PayTransactionPaySuccessMessage.java +++ b/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/message/PayTransactionPaySuccessMessage.java @@ -1,20 +1,18 @@ -package cn.iocoder.mall.pay.biz.mq; - -import cn.iocoder.mall.pay.biz.dataobject.PayTransactionDO; +package cn.iocoder.mall.pay.api.message; /** - * {@link cn.iocoder.mall.pay.biz.constant.MQConstant#TOPIC_PAY_TRANSACTION_PAY_SUCCESS} 的消息对象 + * 支付交易单支付成功的消息对象 */ public class PayTransactionPaySuccessMessage { + public static final String TOPIC = "PAY_TRANSACTION_PAY_SUCCESS"; + /** * 编号,自增 */ private Integer id; /** * 交易编号 - * - * {@link PayTransactionDO#getId()} */ private Integer transactionId; /** @@ -88,4 +86,4 @@ public class PayTransactionPaySuccessMessage { return this; } -} \ No newline at end of file +} diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/constant/MQConstant.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/constant/MQConstant.java deleted file mode 100644 index 57e1b00ae..000000000 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/constant/MQConstant.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.iocoder.mall.pay.biz.constant; - -/** - * MQ 枚举类 - */ -public class MQConstant { - - /** - * Topic - 支付交易单支付成功 - */ - public static final String TOPIC_PAY_TRANSACTION_PAY_SUCCESS = "PAY_TRANSACTION_PAY_SUCCESS"; - -} \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/convert/PayTransactionConvert.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/convert/PayTransactionConvert.java index e6688312a..ec6becef8 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/convert/PayTransactionConvert.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/convert/PayTransactionConvert.java @@ -6,7 +6,7 @@ import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO; import cn.iocoder.mall.pay.biz.dataobject.PayTransactionDO; import cn.iocoder.mall.pay.biz.dataobject.PayTransactionExtensionDO; import cn.iocoder.mall.pay.biz.dataobject.PayTransactionNotifyTaskDO; -import cn.iocoder.mall.pay.biz.mq.PayTransactionPaySuccessMessage; +import cn.iocoder.mall.pay.api.message.PayTransactionPaySuccessMessage; import org.mapstruct.Mapper; import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; @@ -28,4 +28,4 @@ public interface PayTransactionConvert { @Mappings({}) PayTransactionPaySuccessMessage convert(PayTransactionNotifyTaskDO payTransactionNotifyTaskDO); -} \ No newline at end of file +} diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/mq/PayTransactionPaySuccessConsumer.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/mq/PayTransactionPaySuccessConsumer.java index aad591b64..e7e0cde70 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/mq/PayTransactionPaySuccessConsumer.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/mq/PayTransactionPaySuccessConsumer.java @@ -3,7 +3,7 @@ package cn.iocoder.mall.pay.biz.mq; import cn.iocoder.common.framework.util.DateUtil; import cn.iocoder.common.framework.util.ExceptionUtil; import cn.iocoder.mall.pay.api.constant.PayTransactionNotifyStatusEnum; -import cn.iocoder.mall.pay.biz.constant.MQConstant; +import cn.iocoder.mall.pay.api.message.PayTransactionPaySuccessMessage; import cn.iocoder.mall.pay.biz.dao.PayTransactionMapper; import cn.iocoder.mall.pay.biz.dao.PayTransactionNotifyLogMapper; import cn.iocoder.mall.pay.biz.dao.PayTransactionNotifyTaskMapper; @@ -31,8 +31,8 @@ import java.util.Date; @Service @RocketMQMessageListener( - topic = MQConstant.TOPIC_PAY_TRANSACTION_PAY_SUCCESS, - consumerGroup = "pay-consumer-group-" + MQConstant.TOPIC_PAY_TRANSACTION_PAY_SUCCESS + topic = PayTransactionPaySuccessMessage.TOPIC, + consumerGroup = "pay-consumer-group-" + PayTransactionPaySuccessMessage.TOPIC ) public class PayTransactionPaySuccessConsumer implements RocketMQListener { diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/scheduler/PayTransactionNotifyJob.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/scheduler/PayTransactionNotifyJob.java index 358ece94a..e3bcc3ac3 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/scheduler/PayTransactionNotifyJob.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/scheduler/PayTransactionNotifyJob.java @@ -1,6 +1,6 @@ package cn.iocoder.mall.pay.biz.scheduler; -import cn.iocoder.mall.pay.biz.constant.MQConstant; +import cn.iocoder.mall.pay.api.message.PayTransactionPaySuccessMessage; import cn.iocoder.mall.pay.biz.convert.PayTransactionConvert; import cn.iocoder.mall.pay.biz.dao.PayTransactionNotifyTaskMapper; import cn.iocoder.mall.pay.biz.dataobject.PayTransactionNotifyTaskDO; @@ -35,7 +35,7 @@ public class PayTransactionNotifyJob extends IJobHandler { // 循环任务,发送通知 for (PayTransactionNotifyTaskDO payTransactionNotifyTask : notifyTasks) { // 发送 MQ - rocketMQTemplate.convertAndSend(MQConstant.TOPIC_PAY_TRANSACTION_PAY_SUCCESS, + rocketMQTemplate.convertAndSend(PayTransactionPaySuccessMessage.TOPIC, PayTransactionConvert.INSTANCE.convert(payTransactionNotifyTask)); // 更新最后通知时间 // 1. 这样操作,虽然可能会出现 MQ 消费快于下面 PayTransactionNotifyTaskDO 的更新语句。但是,因为更新字段不同,所以不会有问题。 @@ -48,4 +48,4 @@ public class PayTransactionNotifyJob extends IJobHandler { return new ReturnT<>("执行通知数:" + notifyTasks.size()); } -} \ No newline at end of file +} diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/service/PayServiceImpl.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/service/PayServiceImpl.java index 61a09c02d..50f6439e3 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/service/PayServiceImpl.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/biz/service/PayServiceImpl.java @@ -12,10 +12,10 @@ import cn.iocoder.mall.pay.api.constant.PayTransactionNotifyStatusEnum; import cn.iocoder.mall.pay.api.constant.PayTransactionStatusEnum; import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO; import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO; +import cn.iocoder.mall.pay.api.message.PayTransactionPaySuccessMessage; import cn.iocoder.mall.pay.biz.client.AbstractPaySDK; import cn.iocoder.mall.pay.biz.client.PaySDKFactory; import cn.iocoder.mall.pay.biz.client.TransactionPaySuccessBO; -import cn.iocoder.mall.pay.biz.constant.MQConstant; import cn.iocoder.mall.pay.biz.convert.PayTransactionConvert; import cn.iocoder.mall.pay.biz.dao.PayTransactionExtensionMapper; import cn.iocoder.mall.pay.biz.dao.PayTransactionMapper; @@ -188,7 +188,7 @@ public class PayServiceImpl implements PayTransactionService { payTransactionNotifyTaskMapper.insert(payTransactionNotifyTask); logger.info("[updateTransactionPaySuccess][PayTransactionNotifyTaskDO({}) 新增一个任务]", payTransactionNotifyTask.getId()); // 3.2 发送 MQ - rocketMQTemplate.convertAndSend(MQConstant.TOPIC_PAY_TRANSACTION_PAY_SUCCESS, + rocketMQTemplate.convertAndSend(PayTransactionPaySuccessMessage.TOPIC, PayTransactionConvert.INSTANCE.convert(payTransactionNotifyTask)); logger.info("[updateTransactionPaySuccess][PayTransactionNotifyTaskDO({}) 发送 MQ 任务]", payTransactionNotifyTask.getId()); // 返回结果 diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuController.java b/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuController.java index 897b47f2c..27238c00e 100644 --- a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuController.java +++ b/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuController.java @@ -42,6 +42,7 @@ public class UsersProductSpuController { @ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, example = "10"), }) @PermitAll + @Deprecated // 使用商品搜索接口 public CommonResult page(@RequestParam(value = "cid", required = false) Integer cid, @RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { diff --git a/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/ProductCategoryService.java b/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/ProductCategoryService.java index 27da26221..ec8972ca0 100644 --- a/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/ProductCategoryService.java +++ b/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/ProductCategoryService.java @@ -5,6 +5,7 @@ import cn.iocoder.mall.product.api.bo.ProductCategoryBO; import cn.iocoder.mall.product.api.dto.ProductCategoryAddDTO; import cn.iocoder.mall.product.api.dto.ProductCategoryUpdateDTO; +import java.util.Collection; import java.util.List; public interface ProductCategoryService { @@ -15,6 +16,14 @@ public interface ProductCategoryService { */ List getListByPid(Integer pid); + /** + * 获得商品分类数组 + * + * @param ids 商品分类编号 + * @return 数组 + */ + List getListByIds(Collection ids); + /** * @return 返回所有产品分类们 */ @@ -28,4 +37,4 @@ public interface ProductCategoryService { CommonResult deleteProductCategory(Integer admin, Integer productCategoryId); -} \ No newline at end of file +} diff --git a/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/message/ProductUpdateMessage.java b/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/message/ProductUpdateMessage.java new file mode 100644 index 000000000..27fce3bbb --- /dev/null +++ b/product/product-service-api/src/main/java/cn/iocoder/mall/product/api/message/ProductUpdateMessage.java @@ -0,0 +1,20 @@ +package cn.iocoder.mall.product.api.message; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 商品更新(包括创建)消息 + */ +@Data +@Accessors(chain = true) +public class ProductUpdateMessage { + + public static final String TOPIC = "ProductUpdate"; + + /** + * 商品编号 + */ + private Integer id; + +} diff --git a/product/product-service-impl/pom.xml b/product/product-service-impl/pom.xml index 1855c5a9b..5e6269a2d 100644 --- a/product/product-service-impl/pom.xml +++ b/product/product-service-impl/pom.xml @@ -42,6 +42,11 @@ guava + + org.apache.rocketmq + rocketmq-spring-boot-starter + + diff --git a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/dao/ProductCategoryMapper.java b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/dao/ProductCategoryMapper.java index 8c305d682..187d2ac03 100644 --- a/product/product-service-impl/src/main/java/cn/iocoder/mall/product/dao/ProductCategoryMapper.java +++ b/product/product-service-impl/src/main/java/cn/iocoder/mall/product/dao/ProductCategoryMapper.java @@ -4,6 +4,7 @@ import cn.iocoder.mall.product.dataobject.ProductCategoryDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.List; @Repository @@ -16,8 +17,10 @@ public interface ProductCategoryMapper { ProductCategoryDO selectById(@Param("id") Integer id); + List selectByIds(@Param("ids") Collection ids); + void insert(ProductCategoryDO productCategoryDO); int update(ProductCategoryDO productCategoryDO); -} \ 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 2e22a47d8..6af47567c 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 @@ -16,6 +16,7 @@ import cn.iocoder.mall.product.dataobject.ProductCategoryDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Collection; import java.util.Date; import java.util.List; @@ -32,6 +33,12 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { return ProductCategoryConvert.INSTANCE.convertToBO(categoryList); } + @Override + public List getListByIds(Collection ids) { + List categoryList = productCategoryMapper.selectByIds(ids); + return ProductCategoryConvert.INSTANCE.convertToBO(categoryList); + } + @Override public CommonResult> getAll() { List categoryList = productCategoryMapper.selectList(); 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 0ef074be7..82059ea17 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 @@ -13,17 +13,20 @@ import cn.iocoder.mall.product.api.dto.ProductSkuAddOrUpdateDTO; import cn.iocoder.mall.product.api.dto.ProductSpuAddDTO; import cn.iocoder.mall.product.api.dto.ProductSpuPageDTO; import cn.iocoder.mall.product.api.dto.ProductSpuUpdateDTO; +import cn.iocoder.mall.product.api.message.ProductUpdateMessage; import cn.iocoder.mall.product.convert.ProductSpuConvert; import cn.iocoder.mall.product.dao.ProductSkuMapper; import cn.iocoder.mall.product.dao.ProductSpuMapper; import cn.iocoder.mall.product.dataobject.ProductCategoryDO; import cn.iocoder.mall.product.dataobject.ProductSkuDO; import cn.iocoder.mall.product.dataobject.ProductSpuDO; +import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; +import javax.annotation.Resource; import java.util.*; import java.util.stream.Collectors; @@ -41,6 +44,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { @Autowired private ProductAttrServiceImpl productAttrService; + @Resource + private RocketMQTemplate rocketMQTemplate; + // @Override // public ProductSpuBO getProductSpuDetail(Integer id) { // ProductSpuDO productSpuDO = productSpuMapper.selectById(id); @@ -82,10 +88,20 @@ public class ProductSpuServiceImpl implements ProductSpuService { return CommonResult.success(spus); } - @SuppressWarnings("Duplicates") @Override - @Transactional public CommonResult addProductSpu(Integer adminId, ProductSpuAddDTO productSpuAddDTO) { + CommonResult result = addProductSpu0(adminId, productSpuAddDTO); + // 如果新增生成,发送创建商品 Topic 消息 + if (result.isSuccess()) { + // TODO 芋艿,先不考虑事务的问题。等后面的 fescar 一起搞 + sendProductUpdateMessage(result.getData().getId()); + } + return result; + } + + @SuppressWarnings("Duplicates") + @Transactional + public CommonResult addProductSpu0(Integer adminId, ProductSpuAddDTO productSpuAddDTO) { // 校验商品分类分类存在 CommonResult validCategoryResult = productCategoryService.validProductCategory(productSpuAddDTO.getCid()); if (validCategoryResult.isError()) { @@ -129,10 +145,19 @@ public class ProductSpuServiceImpl implements ProductSpuService { validCategoryResult.getData())); } - @SuppressWarnings("Duplicates") @Override - @Transactional public CommonResult updateProductSpu(Integer adminId, ProductSpuUpdateDTO productSpuUpdateDTO) { + CommonResult result = updateProductSpu0(adminId, productSpuUpdateDTO); + if (result.isSuccess()) { + // TODO 芋艿,先不考虑事务的问题。等后面的 fescar 一起搞 + sendProductUpdateMessage(productSpuUpdateDTO.getId()); + } + return result; + } + + @SuppressWarnings("Duplicates") + @Transactional + public CommonResult updateProductSpu0(Integer adminId, ProductSpuUpdateDTO productSpuUpdateDTO) { // 校验 Spu 是否存在 if (productSpuMapper.selectById(productSpuUpdateDTO.getId()) == null) { return ServiceExceptionUtil.error(ProductErrorCodeEnum.PRODUCT_SPU_NOT_EXISTS.getCode()); @@ -208,6 +233,8 @@ public class ProductSpuServiceImpl implements ProductSpuService { // 更新排序 ProductSpuDO updateSpu = new ProductSpuDO().setId(spuId).setSort(sort); productSpuMapper.update(updateSpu); + // 修改成功,发送商品 Topic 消息 + sendProductUpdateMessage(spuId); // 返回成功 return CommonResult.success(true); } @@ -329,4 +356,8 @@ public class ProductSpuServiceImpl implements ProductSpuService { spu.setQuantity(skus.stream().mapToInt(ProductSkuAddOrUpdateDTO::getQuantity).sum()); // 求库存之和 } + private void sendProductUpdateMessage(Integer id) { + rocketMQTemplate.convertAndSend(ProductUpdateMessage.TOPIC, new ProductUpdateMessage().setId(id)); + } + } diff --git a/product/product-service-impl/src/main/resources/config/application.yaml b/product/product-service-impl/src/main/resources/config/application.yaml index af5940f4b..839958124 100644 --- a/product/product-service-impl/src/main/resources/config/application.yaml +++ b/product/product-service-impl/src/main/resources/config/application.yaml @@ -22,4 +22,11 @@ dubbo: port: -1 name: dubbo scan: - base-packages: cn.iocoder.mall.product.service \ No newline at end of file + base-packages: cn.iocoder.mall.product.service + + +# rocketmq +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: product-producer-group diff --git a/product/product-service-impl/src/main/resources/mapper/ProductCategoryMapper.xml b/product/product-service-impl/src/main/resources/mapper/ProductCategoryMapper.xml index a67772ca7..47e484d97 100644 --- a/product/product-service-impl/src/main/resources/mapper/ProductCategoryMapper.xml +++ b/product/product-service-impl/src/main/resources/mapper/ProductCategoryMapper.xml @@ -32,6 +32,17 @@ AND deleted = 0 + + INSERT INTO product_category ( pid, name, description, pic_url, sort, @@ -70,4 +81,4 @@ WHERE id = #{id} - \ No newline at end of file + diff --git a/promotion/promotion-application/src/main/java/cn/iocoder/mall/promotion/application/vo/users/UsersCouponCardVO.java b/promotion/promotion-application/src/main/java/cn/iocoder/mall/promotion/application/vo/users/UsersCouponCardVO.java index 3ae74f181..7381cdafe 100644 --- a/promotion/promotion-application/src/main/java/cn/iocoder/mall/promotion/application/vo/users/UsersCouponCardVO.java +++ b/promotion/promotion-application/src/main/java/cn/iocoder/mall/promotion/application/vo/users/UsersCouponCardVO.java @@ -46,11 +46,6 @@ public class UsersCouponCardVO { // ========== 使用效果 END ========== // ========== 使用情况 BEGIN ========== - /** - * 是否使用 - */ - @ApiModelProperty(value = "是否使用", required = true) - private Boolean used; // TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。 diff --git a/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/CouponService.java b/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/CouponService.java index d8891de8d..7c507f2ea 100644 --- a/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/CouponService.java +++ b/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/CouponService.java @@ -77,11 +77,9 @@ public interface CouponService { * * @param userId 用户编号 * @param couponCardId 优惠劵编号 - * @param usedOrderId 下单的编号 - * @param usedPrice 下单的价格 * @return 是否成功 */ - CommonResult useCouponCard(Integer userId, Integer couponCardId, Integer usedOrderId, Integer usedPrice); + CommonResult useCouponCard(Integer userId, Integer couponCardId); /** * 取消优惠劵的使用 diff --git a/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/bo/CouponCardBO.java b/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/bo/CouponCardBO.java index 6e2c966b1..a060dceea 100644 --- a/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/bo/CouponCardBO.java +++ b/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/bo/CouponCardBO.java @@ -95,18 +95,6 @@ public class CouponCardBO implements Serializable { // ========== 使用效果 END ========== // ========== 使用情况 BEGIN ========== - /** - * 是否使用 - */ - private Boolean used; - /** - * 使用订单号 - */ - private Integer usedOrderId; - /** - * 订单中优惠面值,单位:分 - */ - private Integer usedPrice; /** * 使用时间 */ diff --git a/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/constant/PromotionErrorCodeEnum.java b/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/constant/PromotionErrorCodeEnum.java index 3893cab2f..723213adf 100644 --- a/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/constant/PromotionErrorCodeEnum.java +++ b/promotion/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/constant/PromotionErrorCodeEnum.java @@ -29,6 +29,8 @@ public enum PromotionErrorCodeEnum { COUPON_CARD_NOT_EXISTS(1006003000, "优惠劵不存在"), COUPON_CARD_ERROR_USER(1006003001, "优惠劵不属于当前用户"), COUPON_CARD_NOT_MATCH(1006003002, "优惠劵不匹配,无法使用"), + COUPON_CARD_STATUS_NOT_UNUSED(1006003003, "优惠劵不处于待使用状态"), + COUPON_CARD_STATUS_NOT_USED(1006003004, "优惠劵不处于已使用状态"), ; diff --git a/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/dao/CouponCardMapper.java b/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/dao/CouponCardMapper.java index 84bba8ac3..74c52e38c 100644 --- a/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/dao/CouponCardMapper.java +++ b/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/dao/CouponCardMapper.java @@ -29,4 +29,8 @@ public interface CouponCardMapper { int update(CouponCardDO couponCardDO); + int updateByIdAndStatus(@Param("id") Integer id, + @Param("status") Integer status, + @Param("updateObj") CouponCardDO updateObj); + } diff --git a/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/dataobject/CouponCardDO.java b/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/dataobject/CouponCardDO.java index 81680fcdf..4a41d6291 100644 --- a/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/dataobject/CouponCardDO.java +++ b/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/dataobject/CouponCardDO.java @@ -99,14 +99,14 @@ public class CouponCardDO extends BaseDO { // ========== 使用效果 END ========== // ========== 使用情况 BEGIN ========== - /** - * 使用订单号 - */ - private Integer usedOrderId; - /** - * 订单中优惠面值,单位:分 - */ - private Integer usedPrice; +// /** +// * 使用订单号 +// */ +// private Integer usedOrderId; // TODO 芋艿,暂时不考虑这个字段 +// /** +// * 订单中优惠面值,单位:分 +// */ +// private Integer usedPrice; // TODO 芋艿,暂时不考虑这个字段 /** * 使用时间 */ diff --git a/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/service/CouponServiceImpl.java b/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/service/CouponServiceImpl.java index c7e0b21cf..05039e978 100644 --- a/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/service/CouponServiceImpl.java +++ b/promotion/promotion-service-impl/src/main/java/cn/iocoder/mall/promotion/biz/service/CouponServiceImpl.java @@ -241,13 +241,51 @@ public class CouponServiceImpl implements CouponService { } @Override - public CommonResult useCouponCard(Integer userId, Integer couponCardId, Integer usedOrderId, Integer usedPrice) { - return null; + public CommonResult useCouponCard(Integer userId, Integer couponCardId) { + // 查询优惠劵 + CouponCardDO card = couponCardMapper.selectById(couponCardId); + if (card == null) { + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_NOT_EXISTS.getCode()); + } + if (!userId.equals(card.getUserId())) { + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_ERROR_USER.getCode()); + } + if (CouponCardStatusEnum.UNUSED.getValue().equals(card.getStatus())) { + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode()); + } + if (DateUtil.isBetween(card.getValidStartTime(), card.getValidEndTime())) { // 为避免定时器没跑,实际优惠劵已经过期 + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode()); + } + // 更新优惠劵已使用 + int updateCount = couponCardMapper.updateByIdAndStatus(card.getId(), CouponCardStatusEnum.USED.getValue(), + new CouponCardDO().setStatus(CouponCardStatusEnum.USED.getValue()).setUsedTime(new Date())); + if (updateCount == 0) { + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode()); + } + return CommonResult.success(true); } @Override public CommonResult cancelUseCouponCard(Integer userId, Integer couponCardId) { - return null; + // 查询优惠劵 + CouponCardDO card = couponCardMapper.selectById(couponCardId); + if (card == null) { + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_NOT_EXISTS.getCode()); + } + if (!userId.equals(card.getUserId())) { + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_ERROR_USER.getCode()); + } + if (CouponCardStatusEnum.USED.getValue().equals(card.getStatus())) { + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_USED.getCode()); + } + // 更新优惠劵已使用 + int updateCount = couponCardMapper.updateByIdAndStatus(card.getId(), CouponCardStatusEnum.UNUSED.getValue(), + new CouponCardDO().setStatus(CouponCardStatusEnum.USED.getValue())); // TODO 芋艿,usedTime 未设置空,后面处理。 + if (updateCount == 0) { + return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_USED.getCode()); + } + // 有一点要注意,更新会未使用时,优惠劵可能已经过期了,直接让定时器跑过期,这里不做处理。 + return CommonResult.success(true); } @Override diff --git a/promotion/promotion-service-impl/src/main/resources/mapper/CouponCardMapper.xml b/promotion/promotion-service-impl/src/main/resources/mapper/CouponCardMapper.xml index 720f97944..1bde18757 100644 --- a/promotion/promotion-service-impl/src/main/resources/mapper/CouponCardMapper.xml +++ b/promotion/promotion-service-impl/src/main/resources/mapper/CouponCardMapper.xml @@ -5,7 +5,7 @@ id, template_id, title, status, user_id, take_type, price_available, valid_start_time, valid_end_time, preferential_type, percent_off, price_off, - discount_price_limit, used_order_id, used_price, used_time, + discount_price_limit, used_time, create_time @@ -94,12 +94,12 @@ INSERT INTO coupon_card ( template_id, title, status, user_id, take_type, price_available, valid_start_time, valid_end_time, preferential_type, percent_off, price_off, - discount_price_limit, used_order_id, used_price, used_time, + discount_price_limit, used_time, create_time ) VALUES ( #{templateId}, #{title}, #{status}, #{userId}, #{takeType}, #{priceAvailable}, #{validStartTime}, #{validEndTime}, #{preferentialType}, #{percentOff}, #{priceOff}, - #{discountPriceLimit}, #{usedOrderId}, #{usedPrice}, #{usedTime}, + #{discountPriceLimit}, #{usedTime}, #{createTime} ) @@ -110,12 +110,6 @@ status = #{status}, - - used_order_id = #{usedOrderId}, - - - used_price = #{usedPrice}, - used_time = #{usedTime}, @@ -123,4 +117,18 @@ WHERE id = #{id} + + UPDATE coupon_card + + + status = #{updateObj.status}, + + + used_time = #{updateObj.usedTime}, + + + WHERE id = #{id} + AND status = #{status} + + diff --git a/search/search-application/src/main/java/cn/iocoder/mall/search/application/SearchApplication.java b/search/search-application/src/main/java/cn/iocoder/mall/search/application/SearchApplication.java index f4c7e243f..a9df02936 100644 --- a/search/search-application/src/main/java/cn/iocoder/mall/search/application/SearchApplication.java +++ b/search/search-application/src/main/java/cn/iocoder/mall/search/application/SearchApplication.java @@ -7,6 +7,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; public class SearchApplication { public static void main(String[] args) { + // 解决 ES java.lang.IllegalStateException: availableProcessors is already + System.setProperty("es.set.netty.runtime.available.processors", "false"); SpringApplication.run(SearchApplication.class, args); } diff --git a/search/search-application/src/main/java/cn/iocoder/mall/search/application/controller/users/UsersProductSearchController.java b/search/search-application/src/main/java/cn/iocoder/mall/search/application/controller/users/UsersProductSearchController.java index 5d53fb4a6..b253820bb 100644 --- a/search/search-application/src/main/java/cn/iocoder/mall/search/application/controller/users/UsersProductSearchController.java +++ b/search/search-application/src/main/java/cn/iocoder/mall/search/application/controller/users/UsersProductSearchController.java @@ -4,7 +4,9 @@ import cn.iocoder.common.framework.util.StringUtil; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.SortingField; import cn.iocoder.mall.search.api.ProductSearchService; -import cn.iocoder.mall.search.api.bo.ESProductPageBO; +import cn.iocoder.mall.search.api.bo.ProductConditionBO; +import cn.iocoder.mall.search.api.bo.ProductPageBO; +import cn.iocoder.mall.search.api.dto.ProductConditionDTO; import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO; import com.alibaba.dubbo.config.annotation.Reference; import io.swagger.annotations.Api; @@ -24,12 +26,12 @@ public class UsersProductSearchController { private ProductSearchService productSearchService; @GetMapping("/page") // TODO 芋艿,后面把 BO 改成 VO - public CommonResult page(@RequestParam(value = "cid", required = false) Integer cid, - @RequestParam(value = "keyword", required = false) String keyword, - @RequestParam(value = "pageNo", required = false) Integer pageNo, - @RequestParam(value = "pageSize", required = false) Integer pageSize, - @RequestParam(value = "sortField", required = false) String sortField, - @RequestParam(value = "sortOrder", required = false) String sortOrder) { + public CommonResult page(@RequestParam(value = "cid", required = false) Integer cid, + @RequestParam(value = "keyword", required = false) String keyword, + @RequestParam(value = "pageNo", required = false) Integer pageNo, + @RequestParam(value = "pageSize", required = false) Integer pageSize, + @RequestParam(value = "sortField", required = false) String sortField, + @RequestParam(value = "sortOrder", required = false) String sortOrder) { // 创建 ProductSearchPageDTO 对象 ProductSearchPageDTO productSearchPageDTO = new ProductSearchPageDTO().setCid(cid).setKeyword(keyword) .setPageNo(pageNo).setPageSize(pageSize); @@ -37,7 +39,16 @@ public class UsersProductSearchController { productSearchPageDTO.setSorts(Collections.singletonList(new SortingField(sortField, sortOrder))); } // 执行搜索 - return productSearchService.searchPage(productSearchPageDTO); + return productSearchService.getSearchPage(productSearchPageDTO); + } + + @GetMapping("/condition") // TODO 芋艿,后面把 BO 改成 VO + public CommonResult condition(@RequestParam(value = "keyword", required = false) String keyword) { + // 创建 ProductConditionDTO 对象 + ProductConditionDTO productConditionDTO = new ProductConditionDTO().setKeyword(keyword) + .setFields(Collections.singleton(ProductConditionDTO.FIELD_CATEGORY)); + // 执行搜索 + return productSearchService.getSearchCondition(productConditionDTO); } } 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 index ea24fa3ec..6a79c4bd9 100644 --- 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 @@ -1,13 +1,25 @@ package cn.iocoder.mall.search.api; import cn.iocoder.common.framework.vo.CommonResult; -import cn.iocoder.mall.search.api.bo.ESProductPageBO; +import cn.iocoder.mall.search.api.bo.ProductConditionBO; +import cn.iocoder.mall.search.api.bo.ProductPageBO; +import cn.iocoder.mall.search.api.dto.ProductConditionDTO; import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO; public interface ProductSearchService { CommonResult rebuild(); - CommonResult searchPage(ProductSearchPageDTO searchPageDTO); + /** + * 构建商品的搜索索引 + * + * @param id 商品编号 + * @return 构建结果 + */ + CommonResult save(Integer id); + + CommonResult getSearchPage(ProductSearchPageDTO searchPageDTO); + + CommonResult getSearchCondition(ProductConditionDTO conditionDTO); } diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ESProductBO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductBO.java similarity index 96% rename from search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ESProductBO.java rename to search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductBO.java index 585af5042..acaa15fce 100644 --- a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ESProductBO.java +++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductBO.java @@ -11,7 +11,7 @@ import java.util.List; */ @Data @Accessors(chain = true) -public class ESProductBO implements Serializable { +public class ProductBO implements Serializable { private Integer id; diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductConditionBO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductConditionBO.java new file mode 100644 index 000000000..238b0dbb2 --- /dev/null +++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductConditionBO.java @@ -0,0 +1,35 @@ +package cn.iocoder.mall.search.api.bo; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 商品搜索条件返回 BO + */ +@Data +@Accessors(chain = true) +public class ProductConditionBO { + + /** + * 商品分类数组 + */ + private List categories; + + @Data + @Accessors(chain = true) + public static class Category { + + /** + * 分类编号 + */ + private Integer id; + /** + * 分类名称 + */ + private String name; + + } + +} diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ESProductPageBO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductPageBO.java similarity index 75% rename from search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ESProductPageBO.java rename to search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductPageBO.java index a4e0a155d..b18871a53 100644 --- a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ESProductPageBO.java +++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductPageBO.java @@ -8,12 +8,12 @@ import java.util.List; @Data @Accessors(chain = true) -public class ESProductPageBO implements Serializable { +public class ProductPageBO implements Serializable { /** * 管理员数组 */ - private List list; + private List list; /** * 总量 */ diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductConditionDTO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductConditionDTO.java new file mode 100644 index 000000000..ff1d54c0b --- /dev/null +++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductConditionDTO.java @@ -0,0 +1,29 @@ +package cn.iocoder.mall.search.api.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Collection; + +/** + * 获得商品检索条件 DTO + */ +@Data +@Accessors(chain = true) +public class ProductConditionDTO { + + /** + * Field - 商品分类 + */ + public static final String FIELD_CATEGORY = "category"; + + /** + * 关键字 + */ + private String keyword; + /** + * 需要返回的搜索条件的 fields 名 + */ + private Collection fields; + +} diff --git a/search/search-service-impl/pom.xml b/search/search-service-impl/pom.xml index 00c33ea0d..d7f1928bc 100644 --- a/search/search-service-impl/pom.xml +++ b/search/search-service-impl/pom.xml @@ -43,6 +43,11 @@ spring-boot-starter-data-elasticsearch + + org.apache.rocketmq + rocketmq-spring-boot-starter + + org.springframework.boot diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java index 660851d66..ed239635d 100644 --- a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java +++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java @@ -3,7 +3,7 @@ package cn.iocoder.mall.search.biz.convert; import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO; import cn.iocoder.mall.product.api.bo.ProductSpuDetailBO; import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO; -import cn.iocoder.mall.search.api.bo.ESProductBO; +import cn.iocoder.mall.search.api.bo.ProductBO; import cn.iocoder.mall.search.biz.dataobject.ESProductDO; import org.mapstruct.Mapper; import org.mapstruct.Mappings; @@ -34,6 +34,6 @@ public interface ProductSearchConvert { return product; } - List convert(List list); + List convert(List list); } 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 index dc03498a3..1b4b869d0 100644 --- 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 @@ -54,11 +54,13 @@ public interface ProductRepository extends ElasticsearchRepository nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField.getField()) .order(SortOrder.fromString(sortField.getOrder())))); + } else if (StringUtil.hasText(keyword)) { + nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC)); + } else { + nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("sort").order(SortOrder.DESC)); } // 执行查询 return search(nativeSearchQueryBuilder.build()); diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/mq/PayTransactionPaySuccessConsumer.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/mq/PayTransactionPaySuccessConsumer.java new file mode 100644 index 000000000..498cd1693 --- /dev/null +++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/mq/PayTransactionPaySuccessConsumer.java @@ -0,0 +1,28 @@ +package cn.iocoder.mall.search.biz.mq; + +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.product.api.message.ProductUpdateMessage; +import cn.iocoder.mall.search.api.ProductSearchService; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +@Service +@RocketMQMessageListener( + topic = ProductUpdateMessage.TOPIC, + consumerGroup = "search-consumer-group-" + ProductUpdateMessage.TOPIC +) +public class PayTransactionPaySuccessConsumer implements RocketMQListener { + + @Autowired + private ProductSearchService productSearchService; + + @Override + public void onMessage(ProductUpdateMessage message) { + CommonResult result = productSearchService.save(message.getId()); + Assert.isTrue(result.isSuccess(), String.format("重构商品 ES 索引,必然成功。实际结果是 %s", result)); + } + +} 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 index b6a1d66b8..6e552934e 100644 --- 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 @@ -1,26 +1,39 @@ package cn.iocoder.mall.search.biz.service; import cn.iocoder.common.framework.util.CollectionUtil; +import cn.iocoder.common.framework.util.StringUtil; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.SortingField; import cn.iocoder.mall.order.api.CartService; import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO; +import cn.iocoder.mall.product.api.ProductCategoryService; import cn.iocoder.mall.product.api.ProductSpuService; +import cn.iocoder.mall.product.api.bo.ProductCategoryBO; import cn.iocoder.mall.product.api.bo.ProductSpuDetailBO; import cn.iocoder.mall.search.api.ProductSearchService; -import cn.iocoder.mall.search.api.bo.ESProductPageBO; +import cn.iocoder.mall.search.api.bo.ProductConditionBO; +import cn.iocoder.mall.search.api.bo.ProductPageBO; +import cn.iocoder.mall.search.api.dto.ProductConditionDTO; import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO; import cn.iocoder.mall.search.biz.convert.ProductSearchConvert; import cn.iocoder.mall.search.biz.dao.ProductRepository; import cn.iocoder.mall.search.biz.dataobject.ESProductDO; import com.alibaba.dubbo.config.annotation.Reference; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.stereotype.Service; import org.springframework.util.Assert; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Service @@ -31,10 +44,14 @@ public class ProductSearchServiceImpl implements ProductSearchService { @Autowired private ProductRepository productRepository; + @Autowired + private ElasticsearchTemplate elasticsearchTemplate; // 因为需要使用到聚合操作,只好引入 ElasticsearchTemplate 。 @Reference(validation = "true") private ProductSpuService productSpuService; @Reference(validation = "true") + private ProductCategoryService productCategoryService; + @Reference(validation = "true") private CartService cartService; @Override @@ -57,9 +74,22 @@ public class ProductSearchServiceImpl implements ProductSearchService { lastId = spus.get(spus.size() - 1).getId(); } } + // 返回成功 return CommonResult.success(rebuildCounts); } + @Override + public CommonResult save(Integer id) { + // 获得商品性情 + CommonResult result = productSpuService.getProductSpuDetail(id); + Assert.isTrue(result.isSuccess(), "获得商品详情必然成功"); + // 存储到 ES 中 + ESProductDO product = convert(result.getData()); + productRepository.save(product); + // 返回成功 + return CommonResult.success(Boolean.TRUE); + } + @SuppressWarnings("OptionalGetWithoutIsPresent") private ESProductDO convert(ProductSpuDetailBO spu) { // 获得最小价格的 SKU ,用于下面的价格计算 @@ -72,13 +102,13 @@ public class ProductSearchServiceImpl implements ProductSearchService { } @Override - public CommonResult searchPage(ProductSearchPageDTO searchPageDTO) { + public CommonResult getSearchPage(ProductSearchPageDTO searchPageDTO) { checkSortFieldInvalid(searchPageDTO.getSorts()); // 执行查询 Page searchPage = productRepository.search(searchPageDTO.getCid(), searchPageDTO.getKeyword(), searchPageDTO.getPageNo(), searchPageDTO.getPageSize(), searchPageDTO.getSorts()); // 转换结果 - ESProductPageBO resultPage = new ESProductPageBO() + ProductPageBO resultPage = new ProductPageBO() .setList(ProductSearchConvert.INSTANCE.convert(searchPage.getContent())) .setTotal((int) searchPage.getTotalElements()); return CommonResult.success(resultPage); @@ -92,4 +122,46 @@ public class ProductSearchServiceImpl implements ProductSearchService { String.format("排序字段(%s) 不在允许范围内", sortingField.getField()))); } + @Override + public CommonResult getSearchCondition(ProductConditionDTO conditionDTO) { + // 创建 ES 搜索条件 + NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); + // 筛选 + if (StringUtil.hasText(conditionDTO.getKeyword())) { // 如果有 keyword ,就去匹配 + nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(conditionDTO.getKeyword(), + "name", "sellPoint", "categoryName")); + } else { + nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery()); + } + // 聚合 + if (conditionDTO.getFields().contains(ProductConditionDTO.FIELD_CATEGORY)) { // 商品分类 + nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("cids").field("cid")); + } + // 执行查询 + ProductConditionBO condition = elasticsearchTemplate.query(nativeSearchQueryBuilder.build(), response -> { + ProductConditionBO result = new ProductConditionBO(); + // categoryIds 聚合 + Aggregation categoryIdsAggregation = response.getAggregations().get("cids"); + if (categoryIdsAggregation != null) { + result.setCategories(new ArrayList<>()); + for (LongTerms.Bucket bucket : (((LongTerms) categoryIdsAggregation).getBuckets())) { + result.getCategories().add(new ProductConditionBO.Category().setId(bucket.getKeyAsNumber().intValue())); + } + } + // 返回结果 + return result; + }); + // 聚合其它数据源 + if (!CollectionUtil.isEmpty(condition.getCategories())) { + // 查询指定的 ProductCategoryBO 数组,并转换成 ProductCategoryBO Map + Map categoryMap = productCategoryService.getListByIds( + condition.getCategories().stream().map(ProductConditionBO.Category::getId).collect(Collectors.toList())) + .stream().collect(Collectors.toMap(ProductCategoryBO::getId, category -> category)); + // 设置分类名 + condition.getCategories().forEach(category -> category.setName(categoryMap.get(category.getId()).getName())); + } + // 返回结果 + return CommonResult.success(condition); + } + } diff --git a/search/search-service-impl/src/main/resources/config/application.yaml b/search/search-service-impl/src/main/resources/config/application.yaml index 3a6a8daa1..64bf51411 100644 --- a/search/search-service-impl/src/main/resources/config/application.yaml +++ b/search/search-service-impl/src/main/resources/config/application.yaml @@ -18,3 +18,9 @@ dubbo: name: dubbo scan: base-packages: cn.iocoder.mall.search.biz.service + +# rocketmq +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: search-producer-group