后端:增加商品修改时,发送 MQ 消息
后端:增加搜索服务,监听 MQ 消息,建立商品索引
This commit is contained in:
parent
cddffabeba
commit
f529985c40
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -15,152 +15,160 @@
|
||||
<li :class="filterIndex==12?'selected':''" v-on:click="onFilterBar(12)">价格最高</li>
|
||||
</ul>
|
||||
</div>
|
||||
<van-popup v-model="filterShow" position="right" class="filterlayer" >
|
||||
<div class="filterInner" style="overflow-y: scroll;max-height: 100%;">
|
||||
<div :class="'item_options '+(filterShow?'show':'')">
|
||||
<ul>
|
||||
<li>
|
||||
<van-cell title="清洁类型" is-link arrow-direction="down" />
|
||||
<li v-for="category in categories" :class="category.id === categoryId ?'selected':''" v-on:click="onCategoryClick(category.id)">
|
||||
{{ category.name }}
|
||||
</li>
|
||||
<div style="clear: both;"></div>
|
||||
<div class="tags_selection">
|
||||
<div class="option">
|
||||
<a href="javascript:void 0;">牙龈护理111</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">抛光</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">清洁</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">正畸专用</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">敏感</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">亮白</a>
|
||||
</div>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<van-cell title="清洁类型" is-link arrow-direction="down" />
|
||||
</li>
|
||||
<div style="clear: both;"></div>
|
||||
<div class="tags_selection">
|
||||
<div class="option">
|
||||
<a href="javascript:void 0;">牙龈护理111</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">抛光</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">清洁</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">正畸专用</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">敏感</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">亮白</a>
|
||||
</div>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<van-cell title="清洁类型" is-link arrow-direction="down" />
|
||||
</li>
|
||||
<div style="clear: both;"></div>
|
||||
<div class="tags_selection">
|
||||
<div class="option">
|
||||
<a href="javascript:void 0;">牙龈护理111</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">抛光</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">清洁</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">正畸专用</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">敏感</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">亮白</a>
|
||||
</div>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<van-cell title="清洁类型" is-link arrow-direction="down" />
|
||||
</li>
|
||||
<div style="clear: both;"></div>
|
||||
<div class="tags_selection">
|
||||
<div class="option">
|
||||
<a href="javascript:void 0;">牙龈护理111</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">抛光</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">清洁</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">正畸专用</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">敏感</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">亮白</a>
|
||||
</div>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<van-cell title="清洁类型" is-link arrow-direction="down" />
|
||||
</li>
|
||||
<div style="clear: both;"></div>
|
||||
<div class="tags_selection">
|
||||
<div class="option">
|
||||
<a href="javascript:void 0;">牙龈护理111</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">抛光</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">清洁</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">正畸专用</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">敏感</a>
|
||||
</div>
|
||||
<div class="option ">
|
||||
<a href="javascript:void 0;">亮白</a>
|
||||
</div>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</ul>
|
||||
<div style="clear: both;"></div>
|
||||
<van-button size="large" style="height: 40px;margin-bottom: 15px;line-height: 40px;">清楚选项</van-button>
|
||||
<div style="height:50px;"></div>
|
||||
</div>
|
||||
<div class="filterlayer_bottom_buttons">
|
||||
<span class="filterlayer_bottom_button cancel">取消</span>
|
||||
<span class="filterlayer_bottom_button confirm">确认</span>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<!-- <van-popup v-model="filterShow" position="right" class="filterlayer" >-->
|
||||
<!-- <div class="filterInner" style="overflow-y: scroll;max-height: 100%;">-->
|
||||
<!-- <ul>-->
|
||||
<!-- <li>-->
|
||||
<!-- <van-cell title="清洁类型" is-link arrow-direction="down" />-->
|
||||
<!-- </li>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- <div class="tags_selection">-->
|
||||
<!-- <div class="option">-->
|
||||
<!-- <a href="javascript:void 0;">牙龈护理111</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">抛光</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">清洁</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">正畸专用</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">敏感</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">亮白</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </ul>-->
|
||||
<!-- <ul>-->
|
||||
<!-- <li>-->
|
||||
<!-- <van-cell title="清洁类型" is-link arrow-direction="down" />-->
|
||||
<!-- </li>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- <div class="tags_selection">-->
|
||||
<!-- <div class="option">-->
|
||||
<!-- <a href="javascript:void 0;">牙龈护理111</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">抛光</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">清洁</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">正畸专用</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">敏感</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">亮白</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </ul>-->
|
||||
<!-- <ul>-->
|
||||
<!-- <li>-->
|
||||
<!-- <van-cell title="清洁类型" is-link arrow-direction="down" />-->
|
||||
<!-- </li>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- <div class="tags_selection">-->
|
||||
<!-- <div class="option">-->
|
||||
<!-- <a href="javascript:void 0;">牙龈护理111</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">抛光</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">清洁</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">正畸专用</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">敏感</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">亮白</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </ul>-->
|
||||
<!-- <ul>-->
|
||||
<!-- <li>-->
|
||||
<!-- <van-cell title="清洁类型" is-link arrow-direction="down" />-->
|
||||
<!-- </li>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- <div class="tags_selection">-->
|
||||
<!-- <div class="option">-->
|
||||
<!-- <a href="javascript:void 0;">牙龈护理111</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">抛光</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">清洁</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">正畸专用</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">敏感</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">亮白</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </ul>-->
|
||||
<!-- <ul>-->
|
||||
<!-- <li>-->
|
||||
<!-- <van-cell title="清洁类型" is-link arrow-direction="down" />-->
|
||||
<!-- </li>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- <div class="tags_selection">-->
|
||||
<!-- <div class="option">-->
|
||||
<!-- <a href="javascript:void 0;">牙龈护理111</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">抛光</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">清洁</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">正畸专用</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">敏感</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="option ">-->
|
||||
<!-- <a href="javascript:void 0;">亮白</a>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </ul>-->
|
||||
<!-- <div style="clear: both;"></div>-->
|
||||
<!-- <van-button size="large" style="height: 40px;margin-bottom: 15px;line-height: 40px;">清楚选项</van-button>-->
|
||||
<!-- <div style="height:50px;"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="filterlayer_bottom_buttons">-->
|
||||
<!-- <span class="filterlayer_bottom_button cancel">取消</span>-->
|
||||
<!-- <span class="filterlayer_bottom_button confirm">确认</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </van-popup>-->
|
||||
</div>
|
||||
|
||||
<van-list
|
||||
@ -179,7 +187,7 @@
|
||||
|
||||
<script>
|
||||
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() {
|
||||
|
@ -129,4 +129,5 @@ public class UsersOrderController {
|
||||
orderInfoBO.setStatusText(dictResult.getData().getDisplayName());
|
||||
return commonResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<OrderPageBO> getOrderPage(OrderQueryDTO orderQueryDTO) {
|
||||
@ -248,7 +252,12 @@ public class OrderServiceImpl implements OrderService {
|
||||
.setPresentTotal(priceItem.getPresentTotal());
|
||||
}
|
||||
|
||||
// TODO 芋艿,标记优惠劵使用
|
||||
// 标记优惠劵已使用
|
||||
CommonResult<Boolean> useCouponCardResult = couponService.useCouponCard(userId, orderCreateDTO.getCouponCardId());
|
||||
if (useCouponCardResult.isError()) {
|
||||
return CommonResult.error(useCouponCardResult);
|
||||
}
|
||||
|
||||
// TODO 芋艿,扣除库存
|
||||
|
||||
// order
|
||||
|
@ -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;
|
||||
/**
|
@ -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";
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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<PayTransactionPaySuccessMessage> {
|
||||
|
||||
|
@ -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 的更新语句。但是,因为更新字段不同,所以不会有问题。
|
||||
|
@ -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());
|
||||
// 返回结果
|
||||
|
@ -42,6 +42,7 @@ public class UsersProductSpuController {
|
||||
@ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, example = "10"),
|
||||
})
|
||||
@PermitAll
|
||||
@Deprecated // 使用商品搜索接口
|
||||
public CommonResult<UsersProductSpuPageVO> page(@RequestParam(value = "cid", required = false) Integer cid,
|
||||
@RequestParam(value = "pageNo", defaultValue = "0") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||
|
@ -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<ProductCategoryBO> getListByPid(Integer pid);
|
||||
|
||||
/**
|
||||
* 获得商品分类数组
|
||||
*
|
||||
* @param ids 商品分类编号
|
||||
* @return 数组
|
||||
*/
|
||||
List<ProductCategoryBO> getListByIds(Collection<Integer> ids);
|
||||
|
||||
/**
|
||||
* @return 返回所有产品分类们
|
||||
*/
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -42,6 +42,11 @@
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
<artifactId>rocketmq-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -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,6 +17,8 @@ public interface ProductCategoryMapper {
|
||||
|
||||
ProductCategoryDO selectById(@Param("id") Integer id);
|
||||
|
||||
List<ProductCategoryDO> selectByIds(@Param("ids") Collection<Integer> ids);
|
||||
|
||||
void insert(ProductCategoryDO productCategoryDO);
|
||||
|
||||
int update(ProductCategoryDO productCategoryDO);
|
||||
|
@ -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<ProductCategoryBO> getListByIds(Collection<Integer> ids) {
|
||||
List<ProductCategoryDO> categoryList = productCategoryMapper.selectByIds(ids);
|
||||
return ProductCategoryConvert.INSTANCE.convertToBO(categoryList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<ProductCategoryBO>> getAll() {
|
||||
List<ProductCategoryDO> categoryList = productCategoryMapper.selectList();
|
||||
|
@ -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<ProductSpuDetailBO> addProductSpu(Integer adminId, ProductSpuAddDTO productSpuAddDTO) {
|
||||
CommonResult<ProductSpuDetailBO> result = addProductSpu0(adminId, productSpuAddDTO);
|
||||
// 如果新增生成,发送创建商品 Topic 消息
|
||||
if (result.isSuccess()) {
|
||||
// TODO 芋艿,先不考虑事务的问题。等后面的 fescar 一起搞
|
||||
sendProductUpdateMessage(result.getData().getId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
@Transactional
|
||||
public CommonResult<ProductSpuDetailBO> addProductSpu0(Integer adminId, ProductSpuAddDTO productSpuAddDTO) {
|
||||
// 校验商品分类分类存在
|
||||
CommonResult<ProductCategoryDO> 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<Boolean> updateProductSpu(Integer adminId, ProductSpuUpdateDTO productSpuUpdateDTO) {
|
||||
CommonResult<Boolean> result = updateProductSpu0(adminId, productSpuUpdateDTO);
|
||||
if (result.isSuccess()) {
|
||||
// TODO 芋艿,先不考虑事务的问题。等后面的 fescar 一起搞
|
||||
sendProductUpdateMessage(productSpuUpdateDTO.getId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
@Transactional
|
||||
public CommonResult<Boolean> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,3 +23,10 @@ dubbo:
|
||||
name: dubbo
|
||||
scan:
|
||||
base-packages: cn.iocoder.mall.product.service
|
||||
|
||||
|
||||
# rocketmq
|
||||
rocketmq:
|
||||
name-server: 127.0.0.1:9876
|
||||
producer:
|
||||
group: product-producer-group
|
||||
|
@ -32,6 +32,17 @@
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
<select id="selectByIds" resultType="ProductCategoryDO">
|
||||
SELECT
|
||||
<include refid="FIELDS" />
|
||||
FROM product_category
|
||||
WHERE id IN
|
||||
<foreach collection="ids" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="ProductCategoryDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||
INSERT INTO product_category (
|
||||
pid, name, description, pic_url, sort,
|
||||
|
@ -46,11 +46,6 @@ public class UsersCouponCardVO {
|
||||
// ========== 使用效果 END ==========
|
||||
|
||||
// ========== 使用情况 BEGIN ==========
|
||||
/**
|
||||
* 是否使用
|
||||
*/
|
||||
@ApiModelProperty(value = "是否使用", required = true)
|
||||
private Boolean used;
|
||||
|
||||
// TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
|
||||
|
||||
|
@ -77,11 +77,9 @@ public interface CouponService {
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param couponCardId 优惠劵编号
|
||||
* @param usedOrderId 下单的编号
|
||||
* @param usedPrice 下单的价格
|
||||
* @return 是否成功
|
||||
*/
|
||||
CommonResult<Boolean> useCouponCard(Integer userId, Integer couponCardId, Integer usedOrderId, Integer usedPrice);
|
||||
CommonResult<Boolean> useCouponCard(Integer userId, Integer couponCardId);
|
||||
|
||||
/**
|
||||
* 取消优惠劵的使用
|
||||
|
@ -95,18 +95,6 @@ public class CouponCardBO implements Serializable {
|
||||
// ========== 使用效果 END ==========
|
||||
|
||||
// ========== 使用情况 BEGIN ==========
|
||||
/**
|
||||
* 是否使用
|
||||
*/
|
||||
private Boolean used;
|
||||
/**
|
||||
* 使用订单号
|
||||
*/
|
||||
private Integer usedOrderId;
|
||||
/**
|
||||
* 订单中优惠面值,单位:分
|
||||
*/
|
||||
private Integer usedPrice;
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
|
@ -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, "优惠劵不处于已使用状态"),
|
||||
;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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 芋艿,暂时不考虑这个字段
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
|
@ -241,13 +241,51 @@ public class CouponServiceImpl implements CouponService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> useCouponCard(Integer userId, Integer couponCardId, Integer usedOrderId, Integer usedPrice) {
|
||||
return null;
|
||||
public CommonResult<Boolean> 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<Boolean> 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
|
||||
|
@ -5,7 +5,7 @@
|
||||
<sql id="FIELDS">
|
||||
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
|
||||
</sql>
|
||||
|
||||
@ -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}
|
||||
)
|
||||
</insert>
|
||||
@ -110,12 +110,6 @@
|
||||
<if test="status != null">
|
||||
status = #{status},
|
||||
</if>
|
||||
<if test="usedOrderId != null">
|
||||
used_order_id = #{usedOrderId},
|
||||
</if>
|
||||
<if test="usedPrice != null">
|
||||
used_price = #{usedPrice},
|
||||
</if>
|
||||
<if test="usedTime != null">
|
||||
used_time = #{usedTime},
|
||||
</if>
|
||||
@ -123,4 +117,18 @@
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="updateByIdAndStatus">
|
||||
UPDATE coupon_card
|
||||
<set>
|
||||
<if test="updateObj.status != null">
|
||||
status = #{updateObj.status},
|
||||
</if>
|
||||
<if test="updateObj.usedTime != null">
|
||||
used_time = #{updateObj.usedTime},
|
||||
</if>
|
||||
</set>
|
||||
WHERE id = #{id}
|
||||
AND status = #{status}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,7 +26,7 @@ public class UsersProductSearchController {
|
||||
private ProductSearchService productSearchService;
|
||||
|
||||
@GetMapping("/page") // TODO 芋艿,后面把 BO 改成 VO
|
||||
public CommonResult<ESProductPageBO> page(@RequestParam(value = "cid", required = false) Integer cid,
|
||||
public CommonResult<ProductPageBO> 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,
|
||||
@ -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<ProductConditionBO> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Integer> rebuild();
|
||||
|
||||
CommonResult<ESProductPageBO> searchPage(ProductSearchPageDTO searchPageDTO);
|
||||
/**
|
||||
* 构建商品的搜索索引
|
||||
*
|
||||
* @param id 商品编号
|
||||
* @return 构建结果
|
||||
*/
|
||||
CommonResult<Boolean> save(Integer id);
|
||||
|
||||
CommonResult<ProductPageBO> getSearchPage(ProductSearchPageDTO searchPageDTO);
|
||||
|
||||
CommonResult<ProductConditionBO> getSearchCondition(ProductConditionDTO conditionDTO);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<Category> categories;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class Category {
|
||||
|
||||
/**
|
||||
* 分类编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -8,12 +8,12 @@ import java.util.List;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ESProductPageBO implements Serializable {
|
||||
public class ProductPageBO implements Serializable {
|
||||
|
||||
/**
|
||||
* 管理员数组
|
||||
*/
|
||||
private List<ESProductBO> list;
|
||||
private List<ProductBO> list;
|
||||
/**
|
||||
* 总量
|
||||
*/
|
@ -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<String> fields;
|
||||
|
||||
}
|
@ -43,6 +43,11 @@
|
||||
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
<artifactId>rocketmq-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -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<ESProductBO> convert(List<ESProductDO> list);
|
||||
List<ProductBO> convert(List<ESProductDO> list);
|
||||
|
||||
}
|
||||
|
@ -54,11 +54,13 @@ public interface ProductRepository extends ElasticsearchRepository<ESProductDO,
|
||||
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
|
||||
}
|
||||
// 排序
|
||||
if (CollectionUtil.isEmpty(sortFields)) {
|
||||
nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
|
||||
} else {
|
||||
if (!CollectionUtil.isEmpty(sortFields)) {
|
||||
sortFields.forEach(sortField -> 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());
|
||||
|
@ -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<ProductUpdateMessage> {
|
||||
|
||||
@Autowired
|
||||
private ProductSearchService productSearchService;
|
||||
|
||||
@Override
|
||||
public void onMessage(ProductUpdateMessage message) {
|
||||
CommonResult<Boolean> result = productSearchService.save(message.getId());
|
||||
Assert.isTrue(result.isSuccess(), String.format("重构商品 ES 索引,必然成功。实际结果是 %s", result));
|
||||
}
|
||||
|
||||
}
|
@ -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<Boolean> save(Integer id) {
|
||||
// 获得商品性情
|
||||
CommonResult<ProductSpuDetailBO> 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<ESProductPageBO> searchPage(ProductSearchPageDTO searchPageDTO) {
|
||||
public CommonResult<ProductPageBO> getSearchPage(ProductSearchPageDTO searchPageDTO) {
|
||||
checkSortFieldInvalid(searchPageDTO.getSorts());
|
||||
// 执行查询
|
||||
Page<ESProductDO> 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<ProductConditionBO> 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<Integer, ProductCategoryBO> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user