feat:订单优化

This commit is contained in:
haoxr 2021-03-18 20:23:20 +08:00
parent 490a934869
commit 4a78b09fd3
21 changed files with 90 additions and 103 deletions

View File

@ -3,6 +3,7 @@ package com.youlai.mall.oms.pojo.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.youlai.common.base.BaseEntity;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;

View File

@ -26,7 +26,7 @@ public class CartVO implements Serializable {
private String pic;
private Integer count;
private Integer count; // 商品数量
private Long price; // 加入购物车价格因会变动不能作为订单计算因子订单验价时需重新获取商品价格即可
@ -34,5 +34,7 @@ public class CartVO implements Serializable {
private boolean checked;
private Integer stock;// 商品库存数量页面控制能选择最大数量
}
}

View File

@ -32,6 +32,7 @@ public class RabbitMQConfig {
public Exchange exchange() {
return new TopicExchange("order.exchange", true, false);
}
/**
* 延时队列
*/
@ -41,7 +42,7 @@ public class RabbitMQConfig {
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "order.exchange");
args.put("x-dead-letter-routing-key", "order:close"); // 死信路由Key
args.put("x-message-ttl", 60000); // 单位毫秒配置1分钟测试使用
args.put("x-message-ttl", 60000); // 单位毫秒1分钟测试使用
return new Queue("order.delay.queue", true, false, false, args);
}

View File

@ -46,8 +46,8 @@ public class OrderController {
return Result.success(result);
}
@ApiOperation("订单列表查询")
@GetMapping("/list")
@ApiOperation("订单列表")
@GetMapping
public Result<List<OrderListVO>> list(
@ApiParam(name = "status", value = "订单状态", required = true, defaultValue = "0")
@RequestParam(value = "status", defaultValue = "0") Integer status) {

View File

@ -26,7 +26,7 @@ import java.util.stream.Collectors;
@Component
@AllArgsConstructor
@Slf4j
public class RabbitMQListener {
public class OmsListener {
IOrderService orderService;
@ -41,18 +41,11 @@ public class RabbitMQListener {
* 订单超时未支付关闭订单释放库存
*/
@RabbitListener(queues = "order.close.queue")
public void closeOrder(Long orderId, Message message, Channel channel) {
public void closeOrder(String orderToken, Message message, Channel channel) {
try {
if (orderService.closeOrder(orderId)) {
if (orderService.closeOrder(orderToken)) {
// 如果关单成功发送消息释放库存
// rabbitTemplate.convertAndSend("product_event_change", "stock:unlock", orderSn);
List<OmsOrderItem> orderItems = orderItemService.getByOrderId(orderId);
List<SkuLockDTO> stockLick = orderItems.stream().map(orderItem -> SkuLockDTO.builder()
.skuId(orderItem.getSkuId())
.count(orderItem.getSkuQuantity())
.build())
.collect(Collectors.toList());
skuFeignService.unlockStock(stockLick);
skuFeignService.unlockStock(orderToken);
} else {
// 如果关单失败则订单可能已经被处理直接手动ACK确认消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
@ -62,7 +55,7 @@ public class RabbitMQListener {
try {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
} catch (IOException ioException) {
log.error("释放库存失败,orderId=[{}]", orderId);
log.error("系统关单失败");
}
}
}

View File

@ -15,7 +15,7 @@ import com.youlai.mall.oms.pojo.vo.PayVO;
public interface IOrderPayService extends IService<OmsOrderPay> {
void pay(Long orderId);
boolean pay(Long orderId);
PayVO getByOrderId(Long orderId);
}

View File

@ -36,7 +36,7 @@ public interface IOrderService extends IService<OmsOrder> {
/**
* 系统关闭订单
*/
boolean closeOrder(Long orderId);
boolean closeOrder(String orderToken);
/**
* 取消订单接口

View File

@ -77,6 +77,7 @@ public class CartServiceImpl implements ICartService {
if (cartHashOperations.get(hKey) != null) {
cartItem = (CartVO.CartItem) cartHashOperations.get(hKey);
cartItem.setCount(cartItem.getCount() + 1); // 点击一次加入购物车数量+1
cartItem.setChecked(true);
cartHashOperations.put(hKey, cartItem);
return true;
}
@ -91,9 +92,12 @@ public class CartServiceImpl implements ICartService {
cartItem.setPic(sku.getPic());
cartItem.setSkuId(sku.getId());
cartItem.setTitle(sku.getTitle());
cartItem.setStock(sku.getStock());
cartItem.setChecked(true);
}
});
CompletableFuture.allOf(cartItemCompletableFuture).join();
cartHashOperations.put(hKey,cartItem);
return true;
}

View File

@ -1,20 +1,18 @@
package com.youlai.mall.oms.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.result.Result;
import com.youlai.common.web.exception.BizException;
import com.youlai.common.web.util.RequestUtils;
import com.youlai.mall.oms.mapper.OrderPayMapper;
import com.youlai.mall.oms.enums.OrderStatusEnum;
import com.youlai.mall.oms.enums.PayTypeEnum;
import com.youlai.mall.oms.mapper.OrderPayMapper;
import com.youlai.mall.oms.pojo.domain.OmsOrder;
import com.youlai.mall.oms.pojo.domain.OmsOrderItem;
import com.youlai.mall.oms.pojo.domain.OmsOrderPay;
import com.youlai.mall.oms.pojo.vo.PayVO;
import com.youlai.mall.oms.service.IOrderItemService;
import com.youlai.mall.oms.service.IOrderPayService;
import com.youlai.mall.oms.service.IOrderService;
import com.youlai.mall.pms.api.app.PmsSkuFeignService;
import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
import com.youlai.mall.ums.api.UmsMemberFeignService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
@ -22,8 +20,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@ -33,54 +29,38 @@ public class OrderPayServiceImpl extends ServiceImpl<OrderPayMapper, OmsOrderPay
private IOrderService orderService;
private UmsMemberFeignService memberFeignService;
private IOrderItemService orderItemService;
private PmsSkuFeignService skuFeignService;
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public void pay(Long orderId) {
public boolean pay(Long orderId) {
// 查询订单状态
OmsOrder order = orderService.getByOrderId(orderId);
if (!OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
throw new BizException("订单状态异常,请检查");
if (order != null && !OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
throw new BizException("支付失败,请检查订单状态");
}
// 查询会员余额
// 扣减余额
Long userId = RequestUtils.getUserId();
Long balance = memberFeignService.getBalance(userId).getData();
if (Long.compare(balance, order.getPayAmount()) == -1) {
throw new BizException("会员余额不足");
Long payAmount = order.getPayAmount();
Result deductBalanceResult = memberFeignService.deductBalance(userId, payAmount);
if (Result.isSuccess(deductBalanceResult)) {
throw new BizException("扣减账户余额失败");
}
// 更新用户余额
memberFeignService.updateBalance(userId, order.getPayAmount());
// 扣减库存
Result deductStockResult = skuFeignService.deductStock(order.getOrderSn());
if (Result.isSuccess(deductStockResult)) {
throw new BizException("扣减商品库存失败");
}
// 更新订单状态
// 更新订单
order.setStatus(OrderStatusEnum.PAID.getCode());
order.setPayType(PayTypeEnum.BALANCE.getCode());
order.setPayTime(new Date());
orderService.updateById(order);
boolean result = orderService.updateById(order);
// 扣减库存
List<OmsOrderItem> orderItems = orderItemService.getByOrderId(orderId);
List<SkuLockDTO> stockLick = orderItems.stream().map(orderItem -> SkuLockDTO.builder()
.skuId(orderItem.getSkuId())
.count(orderItem.getSkuQuantity())
.build())
.collect(Collectors.toList());
skuFeignService.deductStock(stockLick);
// 添加订单支付记录
OmsOrderPay orderPay = OmsOrderPay.builder()
.orderId(orderId)
.payAmount(order.getPayAmount())
.payTime(new Date())
.payType(PayTypeEnum.BALANCE.getCode())
.build();
this.save(orderPay);
return result;
}
@Override

View File

@ -5,7 +5,6 @@ import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.result.Result;
import com.youlai.common.web.exception.BizException;
@ -165,7 +164,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
// 创建订单(状态待支付)
OmsOrder order = new OmsOrder();
order.setOrderSn(IdWorker.getTimeId())
order.setOrderSn(orderToken) // 把orderToken赋值给订单编号!
.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode())
.setSourceType(OrderTypeEnum.APP.getCode())
.setMemberId(RequestUtils.getUserId())
@ -183,7 +182,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
orderItemService.saveBatch(orderItemList);
// 将订单放入延时队列超时未支付系统自动关单
rabbitTemplate.convertAndSend("order.exchange", "order:create", orderToken);
rabbitTemplate.convertAndSend("order.exchange", "order.create", orderToken);
OrderSubmitVO submitVO = new OrderSubmitVO();
submitVO.setId(order.getId());
@ -193,27 +192,24 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
@Override
public boolean closeOrder(Long orderId) {
OmsOrder order = this.getOne(new LambdaQueryWrapper<OmsOrder>().eq(OmsOrder::getId, orderId));
public boolean closeOrder(String orderToken) {
OmsOrder order = this.getOne(new LambdaQueryWrapper<OmsOrder>()
.eq(OmsOrder::getOrderSn, orderToken));
if (!OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
return false;
throw new BizException("关单失败,订单状态不支持关闭");
}
order.setStatus(OrderStatusEnum.AUTO_CANCEL.getCode());
this.updateById(order);
orderLogService.addOrderLogs(order.getId(), order.getStatus(), "系统操作", OrderStatusEnum.AUTO_CANCEL.getText());
return true;
return this.updateById(order);
}
@Override
public boolean cancelOrder(Long id) {
log.info("会员取消订单orderId={}", id);
OmsOrder order = getByOrderId(id);
if (!OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
throw new BizException("订单状态非待支付,取消失败");
throw new BizException("取消失败,订单状态不支持取消");
}
order.setStatus(OrderStatusEnum.USER_CANCEL.getCode());
this.updateById(order);
return true;
return this.updateById(order);
}
@Override
@ -223,10 +219,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
OmsOrder order = this.getByOrderId(id);
if (!OrderStatusEnum.AUTO_CANCEL.getCode().equals(order.getStatus()) &&
!OrderStatusEnum.USER_CANCEL.getCode().equals(order.getStatus())) {
throw new BizException("订单状态不允许删除");
throw new BizException("删除失败,订单状态不允许删除");
}
this.removeById(id);
return true;
return this.removeById(id);
}
@Override

View File

@ -27,18 +27,18 @@ public interface PmsSkuFeignService {
/**
* 锁定库存
*/
@PutMapping("/api.app/v1/skus/batch/lock_stock")
@PutMapping("/api.app/v1/skus/lock_stock")
Result lockStock(@RequestBody List<SkuLockDTO> list);
/**
* 解锁库存
*/
@PutMapping("/api.app/v1/skus/batch/unlock_stock")
Result<Boolean> unlockStock(@RequestBody List<SkuLockDTO> list);
@PutMapping("/api.app/v1/skus/unlock_stock")
Result<Boolean> unlockStock(@RequestParam String orderToken);
@PutMapping("/api.app/v1/skus/batch/deduct_stock")
Result deductStock(@RequestBody List<SkuLockDTO> list);
@PutMapping("/api.app/v1/skus/deduct_stock")
Result deductStock(@RequestParam String orderToken);
}

View File

@ -22,14 +22,12 @@ public class SkuController {
private IPmsSkuService iPmsSkuService;
@ApiOperation(value = "库存明细", httpMethod = "GET")
@ApiOperation(value = "商品详情", httpMethod = "GET")
@ApiImplicitParam(name = "id", value = "商品SkuID", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{id}")
public Result<SkuDTO> detail(@PathVariable Long id) {
public Result detail(@PathVariable Long id) {
PmsSku sku = iPmsSkuService.getById(id);
SkuDTO SkuDTO = new SkuDTO();
BeanUtil.copyProperties(sku, SkuDTO);
return Result.success(SkuDTO);
return Result.success(sku);
}
@ApiOperation(value = "修改库存", httpMethod = "PUT")

View File

@ -38,28 +38,28 @@ public class SkuController {
}
@ApiOperation(value = "批量锁定库存", httpMethod = "PUT")
@ApiOperation(value = "锁定库存", httpMethod = "PUT")
@ApiImplicitParam(name = "list", value = "商品列表", required = true, paramType = "body", dataType = "SkuLockDTO")
@PutMapping("/batch/lock_stock")
@PutMapping("/lock_stock")
public Result<Boolean> lockStock(@RequestBody List<SkuLockDTO> list) {
boolean result = iPmsSkuService.lockStock(list);
return Result.judge(result);
}
@ApiOperation(value = "批量解锁库存", httpMethod = "PUT")
@ApiImplicitParam(name = "list", value = "商品列表", required = true, paramType = "body", dataType = "SkuLockDTO")
@PutMapping("/batch/unlock_stock")
public Result<Boolean> unlockStock(@RequestBody List<SkuLockDTO> list) {
boolean result = iPmsSkuService.unlockStock(list);
@ApiOperation(value = "解锁库存", httpMethod = "PUT")
@ApiImplicitParam(name = "orderToken", value = "订单令牌", required = true, paramType = "body", dataType = "String")
@PutMapping("/unlock_stock")
public Result<Boolean> unlockStock(String orderToken) {
boolean result = iPmsSkuService.unlockStock(orderToken);
return Result.judge(result);
}
@ApiOperation(value = "批量扣减库存", httpMethod = "PUT")
@ApiImplicitParam(name = "list", value = "商品列表", required = true, paramType = "body", dataType = "SkuLockDTO")
@PutMapping("/batch/deduct_stock")
public Result<Boolean> deductStock(@RequestBody List<SkuLockDTO> list) {
boolean result = iPmsSkuService.deductStock(list);
@ApiOperation(value = "扣减库存", httpMethod = "PUT")
@ApiImplicitParam(name = "orderToken", value = "订单令牌", required = true, paramType = "body", dataType = "String")
@PutMapping("/deduct_stock")
public Result<Boolean> deductStock(String orderToken) {
boolean result = iPmsSkuService.deductStock(orderToken);
return Result.judge(result);
}

View File

@ -32,7 +32,6 @@ import static com.youlai.mall.pms.common.constant.PmsConstants.STOCK_LOCKED_PREF
@AllArgsConstructor
public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> implements IPmsSkuService {
private StringRedisTemplate redisTemplate;
private RedissonClient redissonClient;

View File

@ -33,16 +33,16 @@ public interface UmsMemberFeignService {
Result updatePoint(@PathVariable Long id, @RequestParam Integer num);
/**
* 修改会员余额
* 扣减会员余额
*/
@PutMapping("/api.app/v1/members/{id}/balances")
Result updateBalance(@PathVariable Long id, @RequestParam Long balance);
@PutMapping("/api.app/v1/members/{id}/balance")
Result deductBalance(@PathVariable Long id, @RequestParam Long balance);
/**
* 获取会员余额
*/
@GetMapping("/api.app/v1/members/{id}/balances")
@GetMapping("/api.app/v1/members/{id}/balance")
Result<Long> getBalance(@PathVariable Long id);

View File

@ -101,7 +101,7 @@ public class MemberController {
@ApiImplicitParam(name = "id", value = "会员ID", required = true, paramType = "path", dataType = "Long"),
@ApiImplicitParam(name = "balance", value = "会员余额", required = true, paramType = "query", dataType = "Long")
})
@PutMapping("/{id}/balances")
@PutMapping("/{id}/balance")
public Result updateBalance(@PathVariable Long id, @RequestParam Long balance) {
UmsMember user = iUmsUserService.getById(id);
user.setBalance(user.getBalance() - balance);
@ -111,7 +111,7 @@ public class MemberController {
@ApiOperation(value = "获取会员余额", httpMethod = "GET")
@ApiImplicitParam(name = "id", value = "会员ID", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{id}/balances")
@GetMapping("/{id}/balance")
public Result<Long> updateBalance(@PathVariable Long id) {
Long balance = 0l;
UmsMember user = iUmsUserService.getById(id);

View File

@ -3,6 +3,7 @@ package com.youlai.common.base;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@ -16,9 +17,11 @@ public class BaseEntity implements Serializable {
@TableField(fill = FieldFill.INSERT)
@JsonInclude(value = JsonInclude.Include.NON_NULL)
@JsonFormat(shape= JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
@JsonInclude(value = JsonInclude.Include.NON_NULL)
@JsonFormat(shape= JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date gmtModified;
}

View File

@ -75,4 +75,11 @@ public class Result<T> implements Serializable {
return result;
}
public static boolean isSuccess(Result result) {
if(ResultCode.SUCCESS.getCode().equals(result.getCode())){
return true;
}
return false;
}
}

View File

@ -44,5 +44,11 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -14,7 +14,6 @@ import org.springframework.stereotype.Component;
* @email huawei_code@163.com
* @date 2021/2/22
*/
@Component
@Configuration
public class RedissonConfig {

View File

@ -36,7 +36,6 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
ServerHttpRequest request = authorizationContext.getExchange().getRequest();
String path = request.getMethodValue() + "_" + request.getURI().getPath();
log.info("请求path={}", path);