diff --git a/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application-dev.yml b/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application-dev.yml index 2a50ba5..dbf9d9b 100644 --- a/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application-dev.yml +++ b/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application-dev.yml @@ -4,7 +4,7 @@ cpop: profile: E:/Cpop/uploadPath 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: rsa-keypair: # 公钥文件 diff --git a/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application-test.yml b/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application-test.yml index 4b126bb..f1e19be 100644 --- a/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application-test.yml +++ b/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application-test.yml @@ -1,8 +1,7 @@ # 项目相关配置 cpop: # 文件路径 示例( Windows配置W:/WorkSpace/java/uploadPath,Linux配置 /home/baseFramework/uploadPath) - #profile: /root/jambox-union/jambox-oam/uploadPath/upload - profile: E:/Cpop/uploadPath + profile: /root/cpop-union/cpop-mall/upload 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/* @@ -10,9 +9,9 @@ cpop: gateway: 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 spring: @@ -80,11 +79,18 @@ knife4j: terms-of-service-url: https://api.jamboxsys.com group: #商城 - Mall: - group-name: Mall + Mall-Backstage: + #后台 + group-name: Mall-Backstage api-rule: package 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: group-name: System @@ -100,6 +106,6 @@ logging: wx: 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 \ No newline at end of file + notifyRefund: https://test.cpopsz.com/onlineShop/Cpop-Mall/wxPay/callback/notify/refund \ No newline at end of file diff --git a/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application.yml b/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application.yml index 64c715d..dc90df2 100644 --- a/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application.yml +++ b/Cpop-Mall/Cpop-Mall-Web/src/main/resources/application.yml @@ -31,7 +31,7 @@ spring: max-file-size: 1024MB max-request-size: 300MB profiles: - active: prod,mall,system + active: test,mall,system datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/Cpop-Mall/src/main/java/com/cpop/mall/business/bo/ProductBo.java b/Cpop-Mall/src/main/java/com/cpop/mall/business/bo/ProductBo.java index 762de30..4e55094 100644 --- a/Cpop-Mall/src/main/java/com/cpop/mall/business/bo/ProductBo.java +++ b/Cpop-Mall/src/main/java/com/cpop/mall/business/bo/ProductBo.java @@ -69,10 +69,17 @@ public class ProductBo implements Serializable { /** * 商品图地址 */ - //@NotBlank(message = "商品图地址不能为空") + @NotBlank(message = "商品图地址不能为空") @ApiModelProperty(value = "商品图地址",required = true) private String picUrl; + /** + * 商品详情图地址 + */ + @NotBlank(message = "商品图地址不能为空") + @ApiModelProperty(value = "商品详情图地址",required = true) + private String picDetailUrl; + /** * 购买限制(0:会员限制;1:新客限定;2:用户限购) */ diff --git a/Cpop-Mall/src/main/java/com/cpop/mall/business/service/impl/OrderServiceImpl.java b/Cpop-Mall/src/main/java/com/cpop/mall/business/service/impl/OrderServiceImpl.java index be2aaa2..559696f 100644 --- a/Cpop-Mall/src/main/java/com/cpop/mall/business/service/impl/OrderServiceImpl.java +++ b/Cpop-Mall/src/main/java/com/cpop/mall/business/service/impl/OrderServiceImpl.java @@ -37,6 +37,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.Lock; import java.util.stream.Collectors; import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER; @@ -124,68 +125,77 @@ public class OrderServiceImpl extends ServiceImpl implements @Override @Transactional(rollbackFor = Exception.class) public Object placeOrder(PlaceOrderBo bo) { - //检查库存 - Map recordNumIsEnough = recordNumIsEnough(bo.getPlaceOrderDetailList()); - try { - Order order = BeanUtils.mapToClass(bo, Order.class); - List orderDetails = BeanUtils.mapToList(bo.getPlaceOrderDetailList(), OrderDetail.class); - //规格记录ids - Set recordIds = orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet()); - //获取涉及到的商品 - ProductService productService = SpringUtils.getBean(ProductService.class); - List productList = productService.queryChain() - .select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION) - .from(PRODUCT) - .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID)) - .where(PRODUCT_RECORD.ID.in(recordIds)) - .list(); - //获取当前下单用户信息 - JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo(); - //设置待付款 - order.setOrderStatus(0) - .setPreviousStatus(0) - //获取当前用户所属品牌 - .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")); + RedisService redisService = SpringUtils.getBean(RedisService.class); + //获取当前下单用户信息 + JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo(); + //分布式锁进行幂等处理 + Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginStaffInfo.getString("userId")); + if (userIdLock.tryLock()) { + //检查库存 + Map recordNumIsEnough = recordNumIsEnough(bo.getPlaceOrderDetailList()); + try { + Order order = BeanUtils.mapToClass(bo, Order.class); + List orderDetails = BeanUtils.mapToList(bo.getPlaceOrderDetailList(), OrderDetail.class); + //规格记录ids + Set recordIds = orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet()); + //获取涉及到的商品 + ProductService productService = SpringUtils.getBean(ProductService.class); + List productList = productService.queryChain() + .select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION) + .from(PRODUCT) + .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID)) + .where(PRODUCT_RECORD.ID.in(recordIds)) + .list(); + //设置待付款 + order.setOrderStatus(0) + .setPreviousStatus(0) + //获取当前用户所属品牌 + .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); - //保存订单详情 - 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 { - //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()); + }else { + //获取锁失败,直接返回空 + return null; } } @@ -205,8 +215,7 @@ public class OrderServiceImpl extends ServiceImpl implements productRecordService.updateBatch(productRecords); //TODO:通知到云? //修改订单状态 - return this.updateChain() - .set(ORDER.ORDER_STATUS, 3).where(ORDER.ID.eq(orderId)).update(); + return this.updateChain().set(ORDER.ORDER_STATUS, 3).where(ORDER.ID.eq(orderId)).update(); } diff --git a/Cpop-Mall/src/main/java/com/cpop/mall/business/service/impl/ProductServiceImpl.java b/Cpop-Mall/src/main/java/com/cpop/mall/business/service/impl/ProductServiceImpl.java index d4685a2..989acc6 100644 --- a/Cpop-Mall/src/main/java/com/cpop/mall/business/service/impl/ProductServiceImpl.java +++ b/Cpop-Mall/src/main/java/com/cpop/mall/business/service/impl/ProductServiceImpl.java @@ -1,15 +1,18 @@ package com.cpop.mall.business.service.impl; import com.alibaba.fastjson.JSONObject; +import com.cpop.common.constant.Constants; import com.cpop.common.utils.StringUtils; import com.cpop.common.utils.bean.BeanUtils; import com.cpop.core.base.entity.PageDomain; 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.SpringUtils; import com.cpop.core.utils.sql.SqlUtils; import com.cpop.jambox.business.entity.BrandExtend; 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.ProductPageBo; import com.cpop.mall.business.bo.ProductRecordBo; diff --git a/Cpop-Mall/src/main/java/com/cpop/mall/business/task/ProductRecordSyncStockTask.java b/Cpop-Mall/src/main/java/com/cpop/mall/business/task/ProductRecordSyncStockTask.java index 87aa225..b427206 100644 --- a/Cpop-Mall/src/main/java/com/cpop/mall/business/task/ProductRecordSyncStockTask.java +++ b/Cpop-Mall/src/main/java/com/cpop/mall/business/task/ProductRecordSyncStockTask.java @@ -28,14 +28,14 @@ public class ProductRecordSyncStockTask { */ @Scheduled(cron="0 5 1 * * ?") public void syncMysqlStockToRedis(){ - log.info("===============================开始<-库存信息同步->开始==============================="); + log.info("===============================开始<--库存信息同步-->开始==============================="); //获取所有库存 List productRecords = SpringUtils.getBean(ProductRecordService.class).list(); RedisService redisService = SpringUtils.getBean(RedisService.class); 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("===============================结束<-库存信息结束->结束==============================="); + log.info("===============================结束<--库存信息结束-->结束==============================="); } } diff --git a/Cpop-Mall/src/main/java/com/cpop/mall/framework/constant/MallRedisConstant.java b/Cpop-Mall/src/main/java/com/cpop/mall/framework/constant/MallRedisConstant.java index 615c3da..f268010 100644 --- a/Cpop-Mall/src/main/java/com/cpop/mall/framework/constant/MallRedisConstant.java +++ b/Cpop-Mall/src/main/java/com/cpop/mall/framework/constant/MallRedisConstant.java @@ -5,5 +5,13 @@ package com.cpop.mall.framework.constant; */ public interface MallRedisConstant { + /** + * redis库存 + */ String STOCK_RECORD_NUM = "mall:stock:recordNum:"; + + /** + * 用户支付幂等键 + */ + String IDEMPOTENT_LOCK_USER_PAY = "mall:idempotentLock:userPay:"; }