Commit 58f3ed3d authored by wlx@33.cn's avatar wlx@33.cn

取消支付接口添加幂等性校验。取消支付接口添加更新前置状态校验和更新结果校验

parent 93fef0f8
package com.fzm.mall.server.front.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author wulixian
* @since 2021/5/25
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIdempotent {
}
package com.fzm.mall.server.front.config;
import com.fzm.mall.server.front.annotation.ApiIdempotent;
import com.fzm.mall.server.front.system.service.ApiTokenService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* @author wulixian
* @since 2021/5/25
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ApiIdempotentInterceptor implements HandlerInterceptor {
private final ApiTokenService apiTokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
ApiIdempotent methodAnnotation = method.getAnnotation(ApiIdempotent.class);
if (null != methodAnnotation) {
//校验通过放行,校验不通过全局异常捕获后输出返回结果
apiTokenService.checkApiToken(request);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
......@@ -44,8 +44,8 @@ public class Redis {
return redisTemplate.opsForValue().increment(key, 1);
}
public void del(String key) {
redisTemplate.delete(key);
public Boolean del(String key) {
return redisTemplate.delete(key);
}
public void setHashKey(String key, String field, String value) {
......
......@@ -11,10 +11,13 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class WebMvcConf implements WebMvcConfigurer {
private final AuthorizationInterceptor authorizationInterceptor;
private final ApiIdempotentInterceptor apiIdempotentInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor).addPathPatterns("/mall/**");
registry.addInterceptor(apiIdempotentInterceptor).addPathPatterns("/**");
}
}
......@@ -68,14 +68,17 @@ public enum MallResponseEnum {
NOT_SUPPORT_BUY_MYSELF("200221","NOT_SUPPORT_BUY_MYSELF"),
ONLY_SUPPORT_MAIL("200222","ONLY_SUPPORT_MAIL"),
SEND_CODE_ERR("200223","SEND_CODE_ERR"),
NULL_PARAM_ERROR("200224","NULL_PARAM_ERROR"),
HOT_ACTIVITY("200237","HOT_ACTIVITY"),
// 系统参数配置
// 系统参数配置,
QUERY_SUCCESS("200200", "query_success"),
TRAN_CANCLE_FAIL("200201", "RECORD_EXIST"),
TRAN_RECORD_FAIL("200202", "ADDR_INVALID"),
REPETITIVE_OPERATION_FAIL("200203", "REPETITIVE_OPERATION_FAIL"),
;
private String code;
......
......@@ -69,7 +69,7 @@ public interface GoodSkuMapper extends BaseMapper<GoodSku> {
// @Update("update goods_sku set stock =stock - #{num} where sku_id = #{skuId} and stock >= #{num}")
// int delStock(@Param("skuId") String skuId, @Param("num") Integer num);
@Update("update goods_sku set stock =stock + #{num} where sku_id = #{skuId}")
void addStock(@Param("skuId") String skuId, @Param("num") Integer number);
int addStock(@Param("skuId") String skuId, @Param("num") Integer number);
@Update("update goods_sku set sales =sales - #{num} where sku_id = #{skuId} and sales >= #{number}")
void delSales(@Param("skuId")String skuId, @Param("num")Integer number);
......
......@@ -50,5 +50,5 @@ public interface SkuMapper extends BaseMapper<Sku> {
String getSerialNo(@Param("commodity_pass_id") String tokenId);
@Update("update goods_sku_usufruct set serial_no = #{toJSONString} where commodity_pass_id = #{commodityPassId}")
void updateSerialNo(@Param("toJSONString") String toJSONString, @Param("commodityPassId") String commodityPassId);
int updateSerialNo(@Param("toJSONString") String toJSONString, @Param("commodityPassId") String commodityPassId);
}
......@@ -21,7 +21,7 @@ public interface IGoodSkuService extends IService<GoodSku> {
int delStock(String skuId,Integer num);
void addStock(String skuId, Integer number);
int addStock(String skuId, Integer number);
void delSales(String skuId, Integer number);
......
......@@ -40,5 +40,5 @@ public interface ISkuService extends IService<Sku> {
String getSerialNo(String tokenId);
void updateSerialNo(String toJSONString,String commodityPassId);
int updateSerialNo(String toJSONString,String commodityPassId);
}
......@@ -66,8 +66,8 @@ public class GoodSkuServiceImpl extends ServiceImpl<GoodSkuMapper, GoodSku> impl
}
@Override
public void addStock(String skuId, Integer number) {
skuMapper.addStock(skuId,number);
public int addStock(String skuId, Integer number) {
return skuMapper.addStock(skuId,number);
}
@Override
......
......@@ -92,8 +92,8 @@ public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements ISkuS
}
@Override
public void updateSerialNo(String toJSONString,String commodityPassId) {
skuMapper.updateSerialNo(toJSONString,commodityPassId);
public int updateSerialNo(String toJSONString,String commodityPassId) {
return skuMapper.updateSerialNo(toJSONString,commodityPassId);
}
private boolean checkMerchantIdAndSkuId(String merchantId, String skuId) {
......
......@@ -4,6 +4,7 @@ package com.fzm.mall.server.front.order.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fzm.mall.server.front.annotation.ApiIdempotent;
import com.fzm.mall.server.front.asset.model.UserAsset;
import com.fzm.mall.server.front.base.PageVO;
import com.fzm.mall.server.front.base.ResponseVO;
......@@ -243,6 +244,7 @@ public class OrderController {
return new ResponseVO<>(MallResponseEnum.SUCCESS);
}
@ApiIdempotent
@ApiOperation(value = "取消支付 参数 pid")
@PostMapping("/closePay")
public ResponseVO<Pay> closePay(@Ignore @RequestAttribute Header header, @RequestBody JSONObject data) {
......
......@@ -172,6 +172,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
String gid = po.getGoodsId();
String skuId = po.getSkuId();
GoodSpu goodSpu = spuService.goodSpu(gid);
SkuVo sku = skuService.getBySkuId(skuId);
Integer type = goodSpu.getType();
vo.setType(type);
if (type == 2) {
......@@ -179,7 +180,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
PreSale preSale = preSaleService.getByGoodsId(gid);
vo.setPreSale(preSale);
}
vo.setThumb(goodSpu.getThumb());
String commonentId = goodCommentService.getCommonentId(oid, skuId);
vo.setCommentId(commonentId);
/**
......@@ -187,12 +187,18 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
*/
boolean hideSku = orderVO.getOrderState() == OrderStateEnum.ORDER_STATE_TO_PAY.getState() &&
goodSpu.getSalesType() == GoodSpuSalesTypeEnum.BLIND_BOX.getType();
if(hideSku){
if (hideSku) {
vo.setSkuId(null);
}else{
} else {
List<Map<String, Object>> skuPropListMap = skuPropService.getSkuPropListBySkuId(skuId);
vo.setPropList(skuPropListMap);
}
if (!StringUtils.isEmpty(sku.getThumb()) && !hideSku) {
vo.setThumb(sku.getThumb());
} else {
vo.setThumb(goodSpu.getThumb());
}
vo.setThumb(goodSpu.getThumb());
vo.setSalesType(goodSpu.getSalesType());
orders.add(vo);
}
......@@ -1193,12 +1199,22 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
log.error(oid.toString());
return;
}
/**
* 校验关单前的订单状态:待支付
*/
checkOrderStateBeforeClose(order);
order.setOrderState(2);
orderMapper.updateById(order);
int update = orderMapper.updateById(order);
if (update == 0) {
throw new RuntimeException("update order fail");
}
List<OrderDetail> list = orderDetailService.getList(oid.toString());
for (OrderDetail orderDetail : list) {
//加库存
goodSkuService.addStock(orderDetail.getSkuId(), orderDetail.getNumber());
int updateSku = goodSkuService.addStock(orderDetail.getSkuId(), orderDetail.getNumber());
if (updateSku == 0) {
throw new RuntimeException("update sku stock fail");
}
String no = orderDetail.getSerialNo();
List<Long> noList = JSON.parseArray(no, Long.class);
SkuVo skuVo = goodSkuService.getSkuVoBySkuId(orderDetail.getSkuId());
......@@ -1207,11 +1223,25 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
List<Long> serialNoList = JSON.parseArray(serialNo, Long.class);
serialNoList.addAll(noList);
Collections.sort(serialNoList);
skuService.updateSerialNo(JSON.toJSONString(serialNoList), skuVo.getCommodityPassId());
int updateSerialNo = skuService.updateSerialNo(JSON.toJSONString(serialNoList), skuVo.getCommodityPassId());
if (updateSerialNo == 0) {
throw new RuntimeException("update sku SerialNo fail");
}
}
}
}
/**
* 关闭订单前校验订单是否为“待支付”
*
* @param order
*/
private void checkOrderStateBeforeClose(Order order) {
if (order.getOrderState() != OrderStateEnum.ORDER_STATE_TO_PAY.getState()) {
throw new RuntimeException("close order fail, order:" + order.toString());
}
}
@Override
@Transactional
public int applyReturn(String uid, String oid) {
......@@ -1392,13 +1422,13 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
long rStart = System.currentTimeMillis();
SkuVo skuVo = orderBindBoxService.genRandomSku(orderVo.getGoodsId());
long rEnd = System.currentTimeMillis();
log.info("计算库存耗时:" +(rEnd-rStart));
log.info("计算库存耗时:" + (rEnd - rStart));
long odStart = System.currentTimeMillis();
String oid = OrderUtil.getOid();
OrderDetail orderDetail = saveOrderDetail(skuVo, oid);
long odEnd = System.currentTimeMillis();
log.info("保存订单详情耗时:" +(odEnd-odStart));
log.info("保存订单详情耗时:" + (odEnd - odStart));
/**
* 2.记录本次下单的sku的数量和规格信息,计算邮费(一幕沒有邮费)
......@@ -1442,32 +1472,33 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
BigDecimal integralDeductionNum = BigDecimal.ZERO;
// BigDecimal integralDeductionNum = saveIntegralRecord(payableAmount, orderVo, uid, payId);
long teEnd = System.currentTimeMillis();
log.info("中间计算耗时:" +(teEnd-teStart));
log.info("中间计算耗时:" + (teEnd - teStart));
/**
* 5.生成支付单order_pay和订单order_info
*/
long oiStart = System.currentTimeMillis();
saveOrderInfo(payId, oid, uid, skuVo.getMerchantId(), skuAmount, discountAmount, postFee, integralDeductionNum, orderVo);
long oiEnd = System.currentTimeMillis();
log.info("保存订单耗时:" +(oiEnd-oiStart));
log.info("保存订单耗时:" + (oiEnd - oiStart));
long opStart = System.currentTimeMillis();
saveOrderPay(payId, oid, orderVo.getCoin(), integralDeductionNum, uid, payableAmount);
long opEnd = System.currentTimeMillis();
log.info("保存支付单耗时:" +(opEnd-opStart));
log.info("保存支付单耗时:" + (opEnd - opStart));
return payId;
}
/**
* 生成订单信息 order_info
*
* @param payId
* @param oid
* @param uid
* @param merchantId
* @param skuAmount 商品总价
* @param discountAmount 促销活动优惠金额 + 优惠券优惠金额
* @param postFee 运费
* @param integralDeductionNum 积分抵扣的金额
* @param skuAmount 商品总价
* @param discountAmount 促销活动优惠金额 + 优惠券优惠金额
* @param postFee 运费
* @param integralDeductionNum 积分抵扣的金额
* @param orderVo
*/
private void saveOrderInfo(String payId, String oid, String uid, String merchantId, BigDecimal skuAmount,
......@@ -1496,12 +1527,13 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
/**
* 生成支付单信息
*
* @param payId
* @param oid
* @param coin 用于抵扣的积分名称
* @param integralDeductionNum 用于抵扣的积分数量
* @param coin 用于抵扣的积分名称
* @param integralDeductionNum 用于抵扣的积分数量
* @param uid
* @param payableAmount 剩余未支付的金额
* @param payableAmount 剩余未支付的金额
*/
private void saveOrderPay(String payId, String oid, String coin, BigDecimal integralDeductionNum, String uid, BigDecimal payableAmount) {
Pay pay = new Pay();
......@@ -1530,7 +1562,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
* @param orderVo
* @param uid
* @param payId
* @return 用于抵扣的积分数量
* @return 用于抵扣的积分数量
*/
private BigDecimal saveIntegralRecord(BigDecimal payableAmount, OrderBlindBoxVo orderVo, String uid, String payId) {
BigDecimal result = BigDecimal.ZERO;
......
package com.fzm.mall.server.front.system.controller;
import com.fzm.mall.server.front.base.ResponseVO;
import com.fzm.mall.server.front.repo.ResponseFactory;
import com.fzm.mall.server.front.system.service.ApiTokenService;
import com.fzm.mall.server.front.user.model.Header;
import jdk.nashorn.internal.ir.annotations.Ignore;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author wulixian
* @since 2021/5/25
*/
@RestController
@RequestMapping("/mall/apiToken")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ApiTokenController {
private final ResponseFactory resFac;
private final ApiTokenService tokenService;
@PostMapping("/creatApiToken")
public ResponseVO creatApiToken(@Ignore @RequestAttribute Header header) {
String result = tokenService.creatApiToken(header.getUid());
return resFac.getSuccessResponseWithData(result);
}
}
package com.fzm.mall.server.front.system.service;
import javax.servlet.http.HttpServletRequest;
/**
* @author wulixian
* @since 2021/5/25
*/
public interface ApiTokenService {
String creatApiToken(String adminId);
void checkApiToken(HttpServletRequest request);
}
package com.fzm.mall.server.front.system.service.impl;
import com.fzm.mall.server.front.config.Redis;
import com.fzm.mall.server.front.constant.MALLGlobalConfig;
import com.fzm.mall.server.front.system.service.ApiTokenService;
import com.fzm.mall.server.front.user.model.User;
import com.fzm.mall.server.front.user.service.IUserService;
import com.fzm.mall.server.front.util.UUIdUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
/**
* @author wulixian
* @since 2021/5/25
*/
@Service
@Transactional
public class ApiTokenServiceImpl implements ApiTokenService {
private static final String API_TOKEN = "api_token";
@Autowired
private Redis redis;
@Autowired
private IUserService userService;
@Override
public String creatApiToken(String adminId) {
String key = MALLGlobalConfig.PJ_SUFFIX + "_" + API_TOKEN + "_" + adminId;
String apiToken = redis.getKey(key);
if (null == apiToken) {
apiToken = UUIdUtil.getUUID();
/**
* 设置60s失效时间,避免未知错误导致的redis内存泄露
*/
redis.setKey(key, apiToken, 60);
}
return apiToken;
}
@Override
public void checkApiToken(HttpServletRequest request) {
//从请求头中获取token
String apiToken = request.getHeader("apiToken");
User user = userService.getByToken(request.getHeader("token"));
String adminId = user.getUid();
if (StringUtils.isBlank(apiToken)) {
//如果请求头token为空就从参数中获取
apiToken = request.getParameter("apiToken");
//如果都为空抛出参数异常的错误
if (StringUtils.isBlank(apiToken)) {
throw new RuntimeException("apiToken_is_null");
}
}
//如果redis中不包含该token,说明token已经被删除了,抛出请求重复异常
String key = MALLGlobalConfig.PJ_SUFFIX + "_" + API_TOKEN + "_" + adminId;
if (null == redis.getKey(key) || !redis.getKey(key).equals(apiToken)) {
throw new RuntimeException("repetitive_operation_fail");
}
//删除token
Boolean del = redis.del(key);
//如果删除不成功(已经被其他请求删除),抛出请求重复异常
if (!del) {
throw new RuntimeException("repetitive_operation_fail");
}
}
}
......@@ -59,4 +59,6 @@ ONLY_SUPPORT_MAIL=\u5F53\u524D\u5546\u54C1\u4EC5\u652F\u6301\u63D0\u8D27
RECORD_EXIST=\u76F8\u5173\u4EA4\u6613\u8BB0\u5F55\u5DF2\u5B58\u5728\uFF0C\u53D6\u6D88\u5931\u8D25
ADDR_INVALID=\u65E0\u6548\u5730\u5740
SEND_CODE_ERR=\u77ED\u4FE1\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5
HOT_ACTIVITY=\u6D3B\u52A8\u592A\u8FC7\u706B\u7206\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5
\ No newline at end of file
HOT_ACTIVITY=\u6D3B\u52A8\u592A\u8FC7\u706B\u7206\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5
REPETITIVE_OPERATION_FAIL=\u8bf7\u52ff\u91cd\u590d\u64cd\u4f5c
NULL_PARAM_ERROR=\u53c2\u6570\u4e0d\u80fd\u4e3a\u7a7a
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment