调整系统定时任务;添加分布式锁;修改配置文件

This commit is contained in:
DB 2023-10-30 17:57:45 +08:00
parent a94b58dfed
commit 358196d8cf
8 changed files with 111 additions and 78 deletions

View File

@ -4,7 +4,7 @@ cpop:
profile: E:/Cpop/uploadPath profile: E:/Cpop/uploadPath
jwt: jwt:
#白名单 #白名单
whiteList: /login,/miniLogin,/getCaptcha,/profile/**,/doc.html,/webjars/**,/favicon.ico,/v2/api-docs/**,/swagger-resources whiteList: /login,/miniLogin,/wxPay/callback/notify/**,/profile/**,/doc.html,/webjars/**,/favicon.ico,/v2/api-docs/**,/swagger-resources
gateway: gateway:
rsa-keypair: rsa-keypair:
# 公钥文件 # 公钥文件

View File

@ -1,8 +1,7 @@
# 项目相关配置 # 项目相关配置
cpop: cpop:
# 文件路径 示例( Windows配置W:/WorkSpace/java/uploadPathLinux配置 /home/baseFramework/uploadPath # 文件路径 示例( Windows配置W:/WorkSpace/java/uploadPathLinux配置 /home/baseFramework/uploadPath
#profile: /root/jambox-union/jambox-oam/uploadPath/upload profile: /root/cpop-union/cpop-mall/upload
profile: E:/Cpop/uploadPath
jwt: jwt:
#白名单 #白名单
whiteList: /login,/miniLogin,/wxPay/callback/notify/**,/profile/**,/doc.html,/webjars/**,/favicon.ico,/v2/api-docs/**,/swagger-resources,/wxOpen/receiveTicket,/wxOpen/*/callback,/wxOpen/bindOpenAccount/*,/wxCp/portal/* whiteList: /login,/miniLogin,/wxPay/callback/notify/**,/profile/**,/doc.html,/webjars/**,/favicon.ico,/v2/api-docs/**,/swagger-resources,/wxOpen/receiveTicket,/wxOpen/*/callback,/wxOpen/bindOpenAccount/*,/wxCp/portal/*
@ -10,9 +9,9 @@ cpop:
gateway: gateway:
rsa-keypair: rsa-keypair:
# 公钥文件 # 公钥文件
publicKeyFile: E:\Cpop\Cpop-Union\Cpop-Core\src\main\resources\static\keyPair\publicKey publicKeyFile: /root/cpop-union/cpop-mall/script/secretKey/publicKey
# 公钥文件 # 公钥文件
privateKeyFile: E:\Cpop\Cpop-Union\Cpop-Core\src\main\resources\static\keyPair\privateKey privateKeyFile: /root/cpop-union/cpop-mall/script/secretKey/privateKey
# DataSource Config # DataSource Config
spring: spring:
@ -80,11 +79,18 @@ knife4j:
terms-of-service-url: https://api.jamboxsys.com terms-of-service-url: https://api.jamboxsys.com
group: group:
#商城 #商城
Mall: Mall-Backstage:
group-name: Mall #后台
group-name: Mall-Backstage
api-rule: package api-rule: package
api-rule-resources: api-rule-resources:
- com.cpop.mall - com.cpop.mall.business.controller.backstage
Mall-Mini:
#后台
group-name: Mall-Mini
api-rule: package
api-rule-resources:
- com.cpop.mall.business.controller.mini
#系统 #系统
System: System:
group-name: System group-name: System
@ -100,6 +106,6 @@ logging:
wx: wx:
pay: pay:
#通知地址 #通知地址
notifyUrl: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/order notifyUrl: https://test.cpopsz.com/onlineShop/Cpop-Mall/wxPay/callback/notify/order
#支付成功 #支付成功
notifyRefund: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/refund notifyRefund: https://test.cpopsz.com/onlineShop/Cpop-Mall/wxPay/callback/notify/refund

View File

@ -31,7 +31,7 @@ spring:
max-file-size: 1024MB max-file-size: 1024MB
max-request-size: 300MB max-request-size: 300MB
profiles: profiles:
active: prod,mall,system active: test,mall,system
datasource: datasource:
type: com.zaxxer.hikari.HikariDataSource type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver

View File

@ -69,10 +69,17 @@ public class ProductBo implements Serializable {
/** /**
* 商品图地址 * 商品图地址
*/ */
//@NotBlank(message = "商品图地址不能为空") @NotBlank(message = "商品图地址不能为空")
@ApiModelProperty(value = "商品图地址",required = true) @ApiModelProperty(value = "商品图地址",required = true)
private String picUrl; private String picUrl;
/**
* 商品详情图地址
*/
@NotBlank(message = "商品图地址不能为空")
@ApiModelProperty(value = "商品详情图地址",required = true)
private String picDetailUrl;
/** /**
* 购买限制(0:会员限制;1:新客限定;2:用户限购) * 购买限制(0:会员限制;1:新客限定;2:用户限购)
*/ */

View File

@ -37,6 +37,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER; import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER;
@ -124,68 +125,77 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Object placeOrder(PlaceOrderBo bo) { public Object placeOrder(PlaceOrderBo bo) {
//检查库存 RedisService redisService = SpringUtils.getBean(RedisService.class);
Map<String, Integer> recordNumIsEnough = recordNumIsEnough(bo.getPlaceOrderDetailList()); //获取当前下单用户信息
try { JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo();
Order order = BeanUtils.mapToClass(bo, Order.class); //分布式锁进行幂等处理
List<OrderDetail> orderDetails = BeanUtils.mapToList(bo.getPlaceOrderDetailList(), OrderDetail.class); Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginStaffInfo.getString("userId"));
//规格记录ids if (userIdLock.tryLock()) {
Set<String> recordIds = orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet()); //检查库存
//获取涉及到的商品 Map<String, Integer> recordNumIsEnough = recordNumIsEnough(bo.getPlaceOrderDetailList());
ProductService productService = SpringUtils.getBean(ProductService.class); try {
List<Product> productList = productService.queryChain() Order order = BeanUtils.mapToClass(bo, Order.class);
.select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION) List<OrderDetail> orderDetails = BeanUtils.mapToList(bo.getPlaceOrderDetailList(), OrderDetail.class);
.from(PRODUCT) //规格记录ids
.leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID)) Set<String> recordIds = orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet());
.where(PRODUCT_RECORD.ID.in(recordIds)) //获取涉及到的商品
.list(); ProductService productService = SpringUtils.getBean(ProductService.class);
//获取当前下单用户信息 List<Product> productList = productService.queryChain()
JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo(); .select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION)
//设置待付款 .from(PRODUCT)
order.setOrderStatus(0) .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID))
.setPreviousStatus(0) .where(PRODUCT_RECORD.ID.in(recordIds))
//获取当前用户所属品牌 .list();
.setBrandId(loginStaffInfo.getString("brandId")) //设置待付款
.setProductNames(productList.stream().map(Product::getProductName).collect(Collectors.joining(","))) order.setOrderStatus(0)
.setPayUserId(loginStaffInfo.getString("userId")); .setPreviousStatus(0)
if (StringUtils.isBlank(bo.getPayUserName())) { //获取当前用户所属品牌
order.setPayUserName(loginStaffInfo.getString("nickName")); .setBrandId(loginStaffInfo.getString("brandId"))
.setProductNames(productList.stream().map(Product::getProductName).collect(Collectors.joining(",")))
.setPayUserId(loginStaffInfo.getString("userId"));
if (StringUtils.isBlank(bo.getPayUserName())) {
order.setPayUserName(loginStaffInfo.getString("nickName"));
}
this.save(order);
//保存订单详情
orderDetails.forEach(item -> {
item.setOrderId(order.getId());
});
SpringUtils.getBean(OrderDetailService.class).saveBatch(orderDetails);
//微信支付
if (bo.getPayType() == 0) {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
//需要分账
orderRequest.setProfitSharing("Y")
.setSpbillCreateIp(IpUtils.getHostIp())
.setSubOpenid(loginStaffInfo.getString("openId"))
//商品描述
.setBody(productList.stream().map(Product::getDescription).collect(Collectors.joining(",")))
//商品详情
.setDetail(JSONObject.toJSONString(BeanUtils.mapToList(bo.getPlaceOrderDetailList(), WxPayGoodsDetailDto.class)))
.setOutTradeNo(order.getId())
//元转分
.setTotalFee(bo.getTotalAmount().scaleByPowerOfTen(2).intValue())
.setTradeType("JSAPI");
//微信支付统一下单
WxPayService wxPayService = wxPayHandler.getWxPayService();
return wxPayService.createOrder(orderRequest);
} else {
return pointPay(recordNumIsEnough, order.getId());
}
} catch (Exception e) {
//回滚库存
recordNumIsEnough.forEach((key, value) -> {
redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
});
throw new ServiceException(e.getMessage());
}finally {
//释放锁
userIdLock.unlock();
} }
this.save(order); }else {
//保存订单详情 //获取锁失败直接返回空
orderDetails.forEach(item -> { return null;
item.setOrderId(order.getId());
});
SpringUtils.getBean(OrderDetailService.class).saveBatch(orderDetails);
//微信支付
if (bo.getPayType() == 0) {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
//需要分账
orderRequest.setProfitSharing("Y")
.setSpbillCreateIp(IpUtils.getHostIp())
.setSubOpenid(loginStaffInfo.getString("openId"))
//商品描述
.setBody(productList.stream().map(Product::getDescription).collect(Collectors.joining(",")))
//商品详情
.setDetail(JSONObject.toJSONString(BeanUtils.mapToList(bo.getPlaceOrderDetailList(), WxPayGoodsDetailDto.class)))
.setOutTradeNo(order.getId())
//元转分
.setTotalFee(bo.getTotalAmount().scaleByPowerOfTen(2).intValue())
.setTradeType("JSAPI");
//微信支付统一下单
WxPayService wxPayService = wxPayHandler.getWxPayService();
return wxPayService.createOrder(orderRequest);
} else {
//TODO:积分支付直接扣库存
return pointPay(recordNumIsEnough, order.getId());
}
} catch (Exception e){
//回滚库存
RedisService redisService = SpringUtils.getBean(RedisService.class);
recordNumIsEnough.forEach((key, value) -> {
redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
});
throw new ServiceException(e.getMessage());
} }
} }
@ -205,8 +215,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
productRecordService.updateBatch(productRecords); productRecordService.updateBatch(productRecords);
//TODO:通知到云 //TODO:通知到云
//修改订单状态 //修改订单状态
return this.updateChain() return this.updateChain().set(ORDER.ORDER_STATUS, 3).where(ORDER.ID.eq(orderId)).update();
.set(ORDER.ORDER_STATUS, 3).where(ORDER.ID.eq(orderId)).update();
} }

View File

@ -1,15 +1,18 @@
package com.cpop.mall.business.service.impl; package com.cpop.mall.business.service.impl;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.StringUtils; import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils; import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.core.base.entity.PageDomain; import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.enums.SourceType; import com.cpop.core.base.enums.SourceType;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.utils.SecurityUtils; import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.SpringUtils; import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils; import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.jambox.business.entity.BrandExtend; import com.cpop.jambox.business.entity.BrandExtend;
import com.cpop.jambox.business.service.BrandExtendService; import com.cpop.jambox.business.service.BrandExtendService;
import com.cpop.jambox.business.vo.CardTemplateListVo;
import com.cpop.mall.business.bo.ProductBo; import com.cpop.mall.business.bo.ProductBo;
import com.cpop.mall.business.bo.ProductPageBo; import com.cpop.mall.business.bo.ProductPageBo;
import com.cpop.mall.business.bo.ProductRecordBo; import com.cpop.mall.business.bo.ProductRecordBo;

View File

@ -28,14 +28,14 @@ public class ProductRecordSyncStockTask {
*/ */
@Scheduled(cron="0 5 1 * * ?") @Scheduled(cron="0 5 1 * * ?")
public void syncMysqlStockToRedis(){ public void syncMysqlStockToRedis(){
log.info("===============================开始<-库存信息同步->开始==============================="); log.info("===============================开始<--库存信息同步-->开始===============================");
//获取所有库存 //获取所有库存
List<ProductRecord> productRecords = SpringUtils.getBean(ProductRecordService.class).list(); List<ProductRecord> productRecords = SpringUtils.getBean(ProductRecordService.class).list();
RedisService redisService = SpringUtils.getBean(RedisService.class); RedisService redisService = SpringUtils.getBean(RedisService.class);
productRecords.forEach(item->{ productRecords.forEach(item->{
redisService.setCacheObject(MallRedisConstant.STOCK_RECORD_NUM + item.getProductId(), item.getRecordNum()); redisService.setCacheObject(MallRedisConstant.STOCK_RECORD_NUM + item.getId(), item.getRecordNum());
log.info("商品记录id为:{};商品库存为:{}", item.getProductId(), item.getRecordNum()); log.info("商品记录id为:{};商品库存为:{}", item.getProductId(), item.getRecordNum());
}); });
log.info("===============================结束<-库存信息结束->结束==============================="); log.info("===============================结束<--库存信息结束-->结束===============================");
} }
} }

View File

@ -5,5 +5,13 @@ package com.cpop.mall.framework.constant;
*/ */
public interface MallRedisConstant { public interface MallRedisConstant {
/**
* redis库存
*/
String STOCK_RECORD_NUM = "mall:stock:recordNum:"; String STOCK_RECORD_NUM = "mall:stock:recordNum:";
/**
* 用户支付幂等键
*/
String IDEMPOTENT_LOCK_USER_PAY = "mall:idempotentLock:userPay:";
} }