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 @@
价格最高
-
-
-
- 取消
- 确认
-
-
+
+
+ -
+ {{ category.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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