Просмотр исходного кода

[砍价][优化]:砍价优化,并发问题,以及多次访问数据库问题

release_toaliyun_real
Stormeye Wu 6 лет назад
Родитель
Сommit
d7c9bbeb68
6 измененных файлов: 190 добавлений и 48 удалений
  1. +18
    -0
      mallinkCApi/src/main/java/com/iformall/config/RedisConfig.java
  2. +134
    -8
      mallinkCApi/src/main/java/com/iformall/controller/WxPressOrderController.java
  3. +5
    -1
      mallinkService/src/main/java/com/iformall/service/WxOrderPressService.java
  4. +8
    -0
      mallinkService/src/main/java/com/iformall/service/WxOrderService.java
  5. +19
    -39
      mallinkService/src/main/java/com/iformall/service/impl/WxOrderPressServiceImpl.java
  6. +6
    -0
      mallinkService/src/main/java/com/iformall/service/impl/WxOrderServiceImpl.java

+ 18
- 0
mallinkCApi/src/main/java/com/iformall/config/RedisConfig.java Просмотреть файл

@@ -177,6 +177,24 @@ public class RedisConfig extends CachingConfigurerSupport {
return template;
}

@Bean("pressOrderRedisTemplate")
public RedisTemplate<String, WxOrder> getPressOrderRedisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, WxOrder> template = new RedisTemplate<>();

Jackson2JsonRedisSerializer<WxOrder> j = new Jackson2JsonRedisSerializer(WxOrder.class);

// value值的序列化
template.setValueSerializer(j);
template.setHashKeySerializer(j);

// key的序列化
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());

template.setConnectionFactory(connectionFactory);
return template;
}


@Bean("stringValueOperations")
public ValueOperations<String, String> getStringValueOperations(RedisConnectionFactory connectionFactory) {


+ 134
- 8
mallinkCApi/src/main/java/com/iformall/controller/WxPressOrderController.java Просмотреть файл

@@ -2,15 +2,16 @@ package com.iformall.controller;

import com.iformall.common.ErrorCode;
import com.iformall.common.ResultData;
import com.iformall.domain.po.WxCUser;
import com.iformall.domain.po.WxCoupon;
import com.iformall.domain.po.WxOrder;
import com.iformall.domain.po.WxOrderPress;
import com.iformall.domain.vo.WxCouponCVo;
import com.iformall.enums.EnumOrderPressStatus;
import com.iformall.enums.EnumOrderStatus;
import com.iformall.exception.MallinkException;
import com.iformall.service.WxCouponService;
import com.iformall.service.WxOrderPressService;
import com.iformall.service.WxOrderService;
import com.iformall.utils.RedisLock;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
@@ -18,11 +19,16 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/api/press")
@@ -39,6 +45,17 @@ public class WxPressOrderController extends BaseController {
@Autowired
WxOrderPressService wxOrderPressService;

@Autowired
@Qualifier("pressOrderRedisTemplate")
RedisTemplate<String, WxOrder> orderRedisTemplate;

@Autowired
@Qualifier("couponDetailRedisTemplate")
RedisTemplate<String, WxCouponCVo> couponCVoRedisTemplate;

@Autowired
RedisLock redisLock;

// 砍价列表 - /api/wxCouponChannel/list
// 我的砍价 - /api/order/pressOrderList
// 砍价订单详情 - /api/order/pressOrderDetail
@@ -49,6 +66,7 @@ public class WxPressOrderController extends BaseController {

@ApiOperation(value = "参与砍价", notes = "{\"orderId\":\"String\"}")
@PostMapping("pressOrderJoin")
@Transactional
public ResultData pressOrderJoin(@RequestBody Map<String, String> paramMap) {
Date curDate = new Date();
String orderIdStr = paramMap.get("orderId");
@@ -56,19 +74,50 @@ public class WxPressOrderController extends BaseController {
return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "orderId不能为空");
}
Long orderId = 0L;
WxOrder order = null;
try {
orderId = Long.valueOf(orderIdStr);
} catch (NumberFormatException e) {
logger.error("orderId convert error, " + orderIdStr + ", e:" + e.getMessage());
return new ResultData(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), "orderId: " + orderIdStr + ", e:" + e.getMessage());
}
WxOrder order = wxOrderService.getById(orderId);
String key = "pressorder:" + orderId;
ValueOperations<String, WxOrder> operations = orderRedisTemplate.opsForValue();
// 缓存
boolean hasKey = orderRedisTemplate.hasKey(key);
if (hasKey) {
// 从缓存获取砍价订单信息
order = operations.get(key);
} else {
// 订单没有入缓存,需要从数据库中读取
order = wxOrderService.getById(orderId);
if (order != null) {
// 订单优化,进缓存
orderRedisTemplate.opsForValue().set(key, order, 3600, TimeUnit.SECONDS);
}
}
if(order == null) {
logger.error("订单不存在:" + orderId);
return new ResultData(ErrorCode.ORDER_IS_NOT_FIND);
}
WxCoupon coupon = wxCouponService.getById(order.getProductId());
if(coupon == null) {
WxCouponCVo wxCouponCVo = null;
String cdKey = "cd:" + order.getProductId();

ValueOperations<String, WxCouponCVo> cdOperations = couponCVoRedisTemplate.opsForValue();
// 缓存
boolean cdHasKey = couponCVoRedisTemplate.hasKey(cdKey);
if (cdHasKey) {
// 从缓存获取券信息
wxCouponCVo = cdOperations.get(cdKey);
} else {
// 券信息没有入缓存,需要从数据库中读取
wxCouponCVo = wxCouponService.getVoById(order.getProductId());
if (wxCouponCVo != null) {
// 券详情优化,进缓存
couponCVoRedisTemplate.opsForValue().set(cdKey, wxCouponCVo, 3600, TimeUnit.SECONDS);
}
}
if(wxCouponCVo == null) {
logger.error("券不存在:" + order.getProductId());
return new ResultData(ErrorCode.COUPON_IS_EMPTY);
}
@@ -93,21 +142,98 @@ public class WxPressOrderController extends BaseController {
return new ResultData(ErrorCode.ORDER_HAD_CANCEL);
}

if(order.getPressCurrentNum() + 1 > coupon.getPressLimitNum()) {
if(order.getPressCurrentNum() + 1 >= wxCouponCVo.getPressLimitNum()) {
logger.error("砍价已结束:" + orderId);
return new ResultData(ErrorCode.COUPON_PRESS_HAD_FINISHED);
}

long time = System.currentTimeMillis() + RedisLock.TIMEOUT;
String timeStr = String.valueOf(time);
if (!redisLock.lock(orderIdStr, timeStr)) {
logger.error("此砍价订单被锁定, orderId: " + orderIdStr);
return new ResultData(ErrorCode.TOO_MANY_REQUEST);
}

Long cUserId = getUserId();
// 检查此人是否已参与砍价
boolean hadJoin = wxOrderPressService.checkCUserHasJoin(order, cUserId);
if (hadJoin) {
redisLock.unlock(orderIdStr, timeStr);
logger.error("用户已参与砍价");
return new ResultData(ErrorCode.COUPON_PRESS_IS_EXIST);
}

try {
wxOrderPressService.pressCouponJoin(order, coupon, getUserId());
WxOrderPress orderPress = wxOrderPressService.pressCouponJoin(order, wxCouponCVo, cUserId);
if (orderPress != null) {
// 更新砍价信息
Integer index = order.getPressCurrentNum() + 1;
if(index < wxCouponCVo.getPressLimitNum()) {
// 未完成
order.setId(order.getId());
order.setPressCurrentNum(index);
order.setPressCurrentValue(order.getPressCurrentValue() - orderPress.getPressValue());
order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESSING.getCode());
order.setUpdateDate(new Date());
orderRedisTemplate.opsForValue().set(key, order, 3600, TimeUnit.SECONDS);
updatePressOrderPressing(order, key, orderPress, index);
} else {
order.setPressCurrentNum(index);
order.setPressCurrentValue(order.getPressCurrentValue() - orderPress.getPressValue());
order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESS_COMPLETE.getCode());
order.setUpdateDate(curDate);
orderRedisTemplate.opsForValue().set(key, order, 3600, TimeUnit.SECONDS);
updatePressOrder(order, wxCouponCVo, orderPress, index);
}
}
} catch (MallinkException e) {
redisLock.unlock(orderIdStr, timeStr);
logger.error(e.getMessage());
return new ResultData(e.getErrorCode(), e.getMessage());
}

redisLock.unlock(orderIdStr, timeStr);
return new ResultData();
}

private void updatePressOrderPressing(WxOrder order, String key, WxOrderPress orderPress, Integer index) {

WxOrder orderUpdatePress = new WxOrder();
orderUpdatePress.setId(order.getId());
orderUpdatePress.setPressCurrentNum(index);
orderUpdatePress.setPressCurrentValue(order.getPressCurrentValue() - orderPress.getPressValue());
orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESSING.getCode());
orderUpdatePress.setUpdateDate(new Date());
// 保存订单
wxOrderService.saveOrUpdate(orderUpdatePress);
}

private void updatePressOrderPressing(WxOrder order, WxOrderPress orderPress, Integer index) {
WxOrder orderUpdatePress = new WxOrder();
orderUpdatePress.setId(order.getId());
orderUpdatePress.setPressCurrentNum(index);
orderUpdatePress.setPressCurrentValue(order.getPressCurrentValue() - orderPress.getPressValue());
orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESSING.getCode());
orderUpdatePress.setUpdateDate(new Date());
// 保存订单
wxOrderService.saveOrUpdate(orderUpdatePress);
}

private void updatePressOrder(WxOrder order, WxCouponCVo wxCouponCVo, WxOrderPress orderPress, Integer index) {
WxOrder orderUpdatePress = new WxOrder();
orderUpdatePress.setId(order.getId());
orderUpdatePress.setPressCurrentNum(index);
orderUpdatePress.setPressCurrentValue(order.getPressCurrentValue() - orderPress.getPressValue());
orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESS_COMPLETE.getCode());
orderUpdatePress.setPayment(wxCouponCVo.getSalePrice());
orderUpdatePress.setUpdateDate(new Date());
// 保存订单
wxOrderService.saveOrUpdate(orderUpdatePress);
// 发送砍价成功消息
if(index.equals(wxCouponCVo.getPressLimitNum())) {
wxOrderPressService.sendPressComplateMsg(orderUpdatePress);
}
}

@ApiOperation(value = "砍价状态(1:我发起的砍价,2:未参与的砍价, 3:已参与的砍价)", notes = "")
@GetMapping("getPressOrderStatus")
@ApiImplicitParam(name = "orderId", value = "orderId", dataType = "String", paramType = "query", required = true)


+ 5
- 1
mallinkService/src/main/java/com/iformall/service/WxOrderPressService.java Просмотреть файл

@@ -47,7 +47,7 @@ public interface WxOrderPressService {
* @param coupon
* @param cUserId
*/
void pressCouponJoin(WxOrder order, WxCoupon coupon, Long cUserId);
WxOrderPress pressCouponJoin(WxOrder order, WxCoupon coupon, Long cUserId);

/**
* 检查用户砍价状态
@@ -55,6 +55,10 @@ public interface WxOrderPressService {
* @param cUserId
*/
int pressCouponStatus(WxOrder order, Long cUserId);

boolean checkCUserHasJoin(WxOrder order, Long cUserId);

void sendPressComplateMsg(WxOrder orderUpdatePress);


+ 8
- 0
mallinkService/src/main/java/com/iformall/service/WxOrderService.java Просмотреть файл

@@ -114,6 +114,14 @@ public interface WxOrderService {
* @return
*/
WxOrder getById(Long id);

/**
* 根据Id获得实体
*
* @param id
* @return
*/
WxOrder getPressOrderById(Long id);
/**
* 保存或更新实体


+ 19
- 39
mallinkService/src/main/java/com/iformall/service/impl/WxOrderPressServiceImpl.java Просмотреть файл

@@ -24,11 +24,14 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class WxOrderPressServiceImpl implements WxOrderPressService {
@@ -52,7 +55,6 @@ public class WxOrderPressServiceImpl implements WxOrderPressService {
@Autowired
MqBaseProducer mqBaseProducer;


@Override
public PageInfo<WxOrderPress> listAsPage(WxOrderPress record, Integer pageIndex, Integer pageSize) {
return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxOrderPressMapper.findList(record));
@@ -82,25 +84,12 @@ public class WxOrderPressServiceImpl implements WxOrderPressService {


@Override
public void pressCouponJoin(WxOrder order, WxCoupon coupon, Long cUserId) {
public WxOrderPress pressCouponJoin(WxOrder order, WxCoupon coupon, Long cUserId) {
final IdWorker idWorker = IdWorker.get();
Date curDate = new Date();
// 检查此人是否已参与砍价
WxOrderPress orderPressQ = new WxOrderPress();
orderPressQ.setTenantId(order.getTenantId());
orderPressQ.setOrderId(order.getId());
orderPressQ.setUserId(cUserId);
QueryWrapper<WxOrderPress> queryWrapper = new QueryWrapper<>();
queryWrapper.setEntity(orderPressQ);
int count = wxOrderPressMapper.selectCount(queryWrapper);
if(count >= 1) {
logger.error("用户已参与砍价: " + cUserId);
throw new MallinkException(ErrorCode.COUPON_PRESS_IS_EXIST);
}

// 添加 wx_order_press
int total = coupon.getPrice() - coupon.getSalePrice();
int left_total = order.getPressCurrentValue();
WxOrderPress orderPress = new WxOrderPress();
orderPress.setId(idWorker.nextId());
orderPress.setTenantId(order.getTenantId());
@@ -109,7 +98,7 @@ public class WxOrderPressServiceImpl implements WxOrderPressService {
orderPress.setUserId(cUserId);
orderPress.setCreateDate(curDate);
orderPress.setFirst(EnumOrderPressType.NORMAL.getCode());
orderPress.setPressValue(PressUtils.stateLessPressValue(total, left_total, coupon.getPressLimitNum(), order.getPressCurrentNum()));
orderPress.setPressValue(PressUtils.stateLessPressValue(total, order.getPressCurrentValue(), coupon.getPressLimitNum(), order.getPressCurrentNum()));

try {
// 保存订单
@@ -118,30 +107,20 @@ public class WxOrderPressServiceImpl implements WxOrderPressService {
logger.error("保存砍价记录Error:" + e.getMessage());
throw new MallinkException(ErrorCode.DB_FAIL);
}
return orderPress;
}

// 更新砍价信息
Integer index = order.getPressCurrentNum() + 1;
WxOrder orderUpdatePress = new WxOrder();
orderUpdatePress.setId(order.getId());
orderUpdatePress.setPressCurrentNum(index);
orderUpdatePress.setPressCurrentValue(left_total - orderPress.getPressValue());
if(index.equals(coupon.getPressLimitNum())) {
orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESS_COMPLETE.getCode());
orderUpdatePress.setPayment(coupon.getSalePrice());
@Override
public boolean checkCUserHasJoin(WxOrder order, Long cUserId) {
WxOrderPress orderPressQ = new WxOrderPress();
orderPressQ.setTenantId(order.getTenantId());
orderPressQ.setOrderId(order.getId());
orderPressQ.setUserId(cUserId);
int count = wxOrderPressMapper.selectCount(new QueryWrapper<>(orderPressQ));
if(count >= 1) {
return true;
} else {
orderUpdatePress.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PRESSING.getCode());
}
orderUpdatePress.setUpdateDate(new Date());
try {
// 保存订单
wxOrderMapper.updateById(orderUpdatePress);
} catch (RuntimeException e) {
logger.error("更新订单Error:" + e.getMessage());
throw new MallinkException(ErrorCode.DB_FAIL);
}
// 发送砍价成功消息
if(index.equals(coupon.getPressLimitNum())) {
sendPressComplateMsg(orderUpdatePress);
return false;
}
}

@@ -167,7 +146,8 @@ public class WxOrderPressServiceImpl implements WxOrderPressService {
*
* @param
*/
private void sendPressComplateMsg(WxOrder orderUpdatePress) {
@Override
public void sendPressComplateMsg(WxOrder orderUpdatePress) {
// 1. get order info
WxOrder order = wxOrderMapper.selectById(orderUpdatePress.getId());
if (order == null) {


+ 6
- 0
mallinkService/src/main/java/com/iformall/service/impl/WxOrderServiceImpl.java Просмотреть файл

@@ -1427,6 +1427,12 @@ public class WxOrderServiceImpl implements WxOrderService {
return wxOrderMapper.selectById(id);
}

@Override
public WxOrder getPressOrderById(Long id) {

return wxOrderMapper.selectById(id);
}

@Override
public void saveOrUpdate(WxOrder record) {
if (record.getId() == null) {


Загрузка…
Отмена
Сохранить