ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/dto/AppBaseDto.java
@@ -13,7 +13,12 @@ @Data public class AppBaseDto { @ApiModelProperty(value = "userId",hidden = true) @ApiModelProperty(value = "userId", hidden = true) private Long userId; @ApiModelProperty(value = "openid", hidden = true) private String openid; @ApiModelProperty(value = "终端IP", hidden = true) private String spbillCreateIp; } ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/poji/shop/ShopProportionVo.java
New file @@ -0,0 +1,31 @@ package com.ruoyi.system.api.domain.poji.shop; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.math.BigDecimal; import java.util.Date; @Data public class ShopProportionVo { /** * 分成id */ private Long proportionId; /** * 商户id */ private Long shopId; /** * 商户类型 */ private Integer shopType; /** * 分成比例 */ private BigDecimal proportionPercent; } ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteShopFallbackFactory.java
@@ -2,6 +2,7 @@ import com.ruoyi.common.core.domain.R; import com.ruoyi.system.api.domain.dto.*; import com.ruoyi.system.api.domain.poji.shop.ShopProportionVo; import com.ruoyi.system.api.domain.vo.*; import com.ruoyi.system.api.model.QwH5LoginVo; import com.ruoyi.system.api.model.QwUserDetailDto; @@ -33,6 +34,11 @@ @Override public R<Shop> getShop(Long shopId) { return R.fail("获取商户失败:" + throwable.getMessage()); } @Override public R<String> getShopSubMchId(Long shopId) { return R.fail("获取微信商户号失败:" + throwable.getMessage()); } @Override @@ -109,6 +115,11 @@ public R editMgtShopStaff(MgtShopStaffEditDto mgtShopStaffEditDto) { return R.fail("修改员工失败:" + throwable.getMessage()); } @Override public R<ShopProportionVo> getShopProportion(Long shopId) { return R.fail("获取商户分成信息失败:" + throwable.getMessage()); } }; } } ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/service/RemoteShopService.java
@@ -4,6 +4,7 @@ import com.ruoyi.common.core.domain.R; import com.ruoyi.system.api.domain.dto.*; import com.ruoyi.system.api.domain.poji.shop.Shop; import com.ruoyi.system.api.domain.poji.shop.ShopProportionVo; import com.ruoyi.system.api.domain.vo.*; import com.ruoyi.system.api.factory.RemoteShopFallbackFactory; import com.ruoyi.system.api.model.QwH5LoginVo; @@ -19,6 +20,10 @@ @PostMapping("/shop/getShop") public R<Shop> getShop(@RequestBody Long shopId); @PostMapping("/shop/getShopSubMchId") public R<String> getShopSubMchId(@RequestBody Long shopId); @PostMapping("/shop/getShopByUserId") public R<ShopRelUserVo> getShopByUserId(@RequestBody Long userId); @@ -149,4 +154,12 @@ */ @PostMapping("/shop/editMgtShopStaff") public R editMgtShopStaff(@RequestBody MgtShopStaffEditDto mgtShopStaffEditDto); /** * 获取商户分成 * @param shopId * @return */ @PostMapping("/shop/getShopProportion") R<ShopProportionVo> getShopProportion(@RequestBody Long shopId); } ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java
@@ -1,6 +1,8 @@ package com.ruoyi.common.security.utils; import javax.servlet.http.HttpServletRequest; import com.ruoyi.system.api.domain.poji.sys.SysUser; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import com.ruoyi.common.core.constant.SecurityConstants; import com.ruoyi.common.core.constant.TokenConstants; @@ -11,7 +13,7 @@ /** * 权限获取工具类 * * * @author jqs */ public class SecurityUtils @@ -48,6 +50,11 @@ return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class); } public static SysUser getSysUser() { return getLoginUser().getSysUser(); } /** * 获取请求token */ @@ -81,7 +88,7 @@ /** * 是否为管理员 * * * @param userId 用户ID * @return 结果 */ ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/config/MyWxPayConfig.java
File was deleted ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/config/WxPayConfiguration.java
New file @@ -0,0 +1,54 @@ package com.ruoyi.order.config; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; import lombok.AllArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author Binary Wang */ @Configuration @ConditionalOnClass(WxPayService.class) @EnableConfigurationProperties(WxPayProperties.class) @AllArgsConstructor public class WxPayConfiguration { private WxPayProperties properties; // 分账特约商户 private static String PLATFORM_TY_MAC_ID = ""; public static String getPlatformTyMacId() { return PLATFORM_TY_MAC_ID; } @Bean @ConditionalOnMissingBean public WxPayService wxService() { WxPayConfig payConfig = new WxPayConfig(); payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId())); payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId())); payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey())); payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId())); payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId())); payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); // 平台特约商户号 PLATFORM_TY_MAC_ID = this.properties.getPlatformTyMacId(); // 可以指定是否使用沙箱环境 payConfig.setUseSandboxEnv(false); WxPayService wxPayService = new WxPayServiceImpl(); wxPayService.setConfig(payConfig); return wxPayService; } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/config/WxPayProperties.java
New file @@ -0,0 +1,49 @@ package com.ruoyi.order.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; /** * wxpay pay properties. * * @author Binary Wang */ @Data @ConfigurationProperties(prefix = "wx.pay") public class WxPayProperties { /** * 设置微信公众号或者小程序等的appid */ private String appId; /** * 微信支付商户号 */ private String mchId; /** * 微信支付商户密钥 */ private String mchKey; /** * 平台特约商户号,用户平台收取服务费 */ private String platformTyMacId; /** * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除 */ private String subAppId; /** * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除 */ private String subMchId; /** * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定 */ private String keyPath; } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/miniapp/AppOrderController.java
@@ -15,6 +15,7 @@ import com.ruoyi.system.api.domain.dto.AppBaseGetDto; import com.ruoyi.system.api.domain.dto.AppMemberBindingDto; import com.ruoyi.system.api.domain.poji.member.Member; import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.service.RemoteConfigService; import com.ruoyi.system.api.service.RemoteMemberService; import io.swagger.annotations.Api; @@ -57,10 +58,10 @@ @ApiOperation(value = "购买商品") public R<AppSureOrderVo> buyGoods(@RequestBody AppSureOrderDto appSureOrderDto) { Long userId = SecurityUtils.getUserId(); if(userId!=null){ if (userId != null) { Member member = memberService.getMember(userId).getData(); appSureOrderDto.setUserId(userId); if(member!=null&&member.getRelationShopId()!=null){ if (member != null && member.getRelationShopId() != null) { appSureOrderDto.setShopId(member.getRelationShopId()); } } @@ -73,10 +74,10 @@ @ApiOperation(value = "抢购商品") public R<AppPanicBuyVo> panicBuyGoods(@RequestBody AppPanicBuyDto appPanicBuyDto) { Long userId = SecurityUtils.getUserId(); if(userId!=null){ if (userId != null) { Member member = memberService.getMember(userId).getData(); appPanicBuyDto.setUserId(userId); if(member!=null&&member.getRelationShopId()!=null){ if (member != null && member.getRelationShopId() != null) { appPanicBuyDto.setShopId(member.getRelationShopId()); } } @@ -87,20 +88,26 @@ @RequestMapping(value = "/placeOrder", method = RequestMethod.POST) @ApiOperation(value = "创建订单") public R<AppPlaceOrderVo> placeOrder(@RequestBody AppPlaceOrderDto appPlaceOrderDto) { Long userId = SecurityUtils.getUserId(); if(userId!=null){ LoginUser loginUser = SecurityUtils.getLoginUser(); Long userId = loginUser.getUserid(); appPlaceOrderDto.setUserId(userId); appPlaceOrderDto.setSpbillCreateIp(loginUser.getIpaddr()); if (userId != null) { Member member = memberService.getMember(userId).getData(); appPlaceOrderDto.setUserId(userId); if(member!=null&&member.getRelationShopId()!=null){ if (member != null) { appPlaceOrderDto.setOpenid(member.getMiniOpenid()); appPlaceOrderDto.setShopId(member.getRelationShopId()); } if(member.getBindingFlag()!=1){ //绑定商户 AppMemberBindingDto appMemberBindingDto = new AppMemberBindingDto(); appMemberBindingDto.setShopId(appPlaceOrderDto.getShopId()); appMemberBindingDto.setUserId(userId); appMemberBindingDto.setBindingFlag(1); memberService.updateMemberBinding(appMemberBindingDto); if (member.getBindingFlag() != 1) { //绑定商户 AppMemberBindingDto appMemberBindingDto = new AppMemberBindingDto(); appMemberBindingDto.setShopId(appPlaceOrderDto.getShopId()); appMemberBindingDto.setUserId(userId); appMemberBindingDto.setBindingFlag(1); memberService.updateMemberBinding(appMemberBindingDto); } } } AppPlaceOrderVo appPlaceOrderVo = orderService.placeOrder(appPlaceOrderDto); @@ -111,13 +118,13 @@ @ApiOperation(value = "创建活动订单") public R<AppPlaceOrderVo> placeActivityOrder(@Validated @RequestBody AppPlaceActivityDto appPlaceActivityDto) { Long userId = SecurityUtils.getUserId(); if(userId!=null){ if (userId != null) { Member member = memberService.getMember(userId).getData(); appPlaceActivityDto.setUserId(userId); if(member!=null&&member.getRelationShopId()!=null){ if (member != null && member.getRelationShopId() != null) { appPlaceActivityDto.setShopId(member.getRelationShopId()); } if(member.getBindingFlag()!=1){ if (member.getBindingFlag() != 1) { //绑定商户 AppMemberBindingDto appMemberBindingDto = new AppMemberBindingDto(); appMemberBindingDto.setShopId(appPlaceActivityDto.getShopId()); @@ -134,18 +141,18 @@ @ApiOperation(value = "分页获取用户订单") public R<Page<AppUserOrderPageVo>> pageUserOrder(@RequestBody AppUserOrderPageDto appUserOrderPageDto) { Long userId = SecurityUtils.getUserId(); if(userId!=null){ if (userId != null) { Member member = memberService.getMember(userId).getData(); appUserOrderPageDto.setUserId(userId); if(member!=null&&member.getRelationShopId()!=null){ if (member != null && member.getRelationShopId() != null) { appUserOrderPageDto.setShopId(member.getRelationShopId()); } } logger.info("appUserOrderPageDto:"+appUserOrderPageDto.toString()); logger.info("appUserOrderPageDto:" + appUserOrderPageDto.toString()); Page<AppUserOrderPageVo> page = new Page<>(); page.setSize(appUserOrderPageDto.getPageSize()); page.setCurrent(appUserOrderPageDto.getPageNum()); List<AppUserOrderPageVo> appUserOrderPageVoList = orderService.pageUserOrder(page,appUserOrderPageDto); List<AppUserOrderPageVo> appUserOrderPageVoList = orderService.pageUserOrder(page, appUserOrderPageDto); return R.ok(page.setRecords(appUserOrderPageVoList)); } @@ -154,17 +161,17 @@ public R<AppUserOrderGetVo> getAppOrderDetail(@RequestBody AppBaseGetDto appBaseGetDto) { Long userId = SecurityUtils.getUserId(); Long shopId = null; if(userId!=null){ if (userId != null) { Member member = memberService.getMember(userId).getData(); appBaseGetDto.setUserId(userId); if(member!=null&&member.getRelationShopId()!=null){ if (member != null && member.getRelationShopId() != null) { shopId = member.getRelationShopId(); } } AppUserOrderGetVo appUserOrderGetVo = orderService.getAppOrderDetail(appBaseGetDto); if(shopId!=null&&appUserOrderGetVo.getShopId().equals(shopId)){ if (shopId != null && appUserOrderGetVo.getShopId().equals(shopId)) { appUserOrderGetVo.setSameShop(1); }else{ } else { appUserOrderGetVo.setSameShop(0); } return R.ok(appUserOrderGetVo); @@ -184,12 +191,12 @@ appPlaceOrderVo.setPayMoney(order.getPayMoney()); appPlaceOrderVo.setUnpaidMoney(order.getPayMoney()); Integer delayTime = 30; if(redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)){ if (redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)) { delayTime = redisService.getCacheObject(SecurityConstant.AUTO_CANCEL_ORDER_TIME); } redisService.deleteObject(DelayTaskEnum.ORDER_AUTOMATIC_CANCEL.getCode() + "-" + order.getOrderId()); remoteConfigService.deleteDelayTask(DelayTaskEnum.ORDER_AUTOMATIC_CANCEL.getCode() + "-" + order.getOrderId()); appPlaceOrderVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(),delayTime)); appPlaceOrderVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(), delayTime)); return R.ok(appPlaceOrderVo); } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/miniapp/NotifyController.java
@@ -1,38 +1,34 @@ package com.ruoyi.order.controller.miniapp; import com.ruoyi.common.core.domain.R; import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Response; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyData; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.ProfitSharingV3Service; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.v3.auth.Verifier; import com.github.binarywang.wxpay.v3.util.AesUtils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.order.domain.dto.AppSureOrderDto; import com.ruoyi.order.domain.dto.WXPayNotifyDto; import com.ruoyi.order.domain.vo.AppSureOrderVo; import com.ruoyi.order.domain.vo.ProfitSharingNotifyNewResult; import com.ruoyi.order.enums.WxPayNotifyEventTypeEnum; import com.ruoyi.order.service.account.BackMessageService; import com.ruoyi.order.service.order.OrderService; import com.wechat.pay.contrib.apache.httpclient.notification.Notification; import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler; import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAAutoCertificateConfig; import com.wechat.pay.java.core.RSAConfig; import com.wechat.pay.java.core.cipher.PrivacyEncryptor; import com.wechat.pay.java.core.notification.NotificationConfig; import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.RequestParam; import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import net.bytebuddy.asm.Advice; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.security.GeneralSecurityException; import java.util.Map; import java.util.Objects; /** * @program: ruoyi @@ -44,88 +40,82 @@ @RestController @RequestMapping("/app/notify") public class NotifyController extends BaseController { /** 商户号 */ public static String merchantId = ""; /** 商户API私钥路径 */ public static String privateKeyPath = ""; /** 商户证书序列号 */ public static String merchantSerialNumber = ""; /** 微信支付平台证书路径 */ public static String wechatPayCertificatePath = ""; /** 商户APIV3密钥 */ public static String apiV3key = "..."; public static final String WECHAT_PAY_SERIAL = "Wechatpay-Serial"; public static final String WECHAT_PAY_SIGNATURE = "Wechatpay-Signature"; public static final String WECHAT_PAY_TIMESTAMP = "Wechatpay-Timestamp"; public static final String WECHAT_PAY_NONCE = "Wechatpay-Nonce"; private static final Gson GSON = new GsonBuilder().create(); @Resource private OrderService orderService; @RequestMapping(value = "/payNotify", method = RequestMethod.POST) @ApiOperation(value = "微信支付通知") public Map payNotify(HttpServletRequest request, HttpServletResponse response) { Config config = new RSAConfig.Builder() .merchantId(merchantId) .privateKeyFromPath(privateKeyPath) .merchantSerialNumber(merchantSerialNumber) .wechatPayCertificatesFromPath(wechatPayCertificatePath) .build(); @Autowired private WxPayService wxService; @Resource private BackMessageService backMessageService; PrivacyEncryptor encryptor = config.createEncryptor(); String wechatPayCertificateSerialNumber = encryptor.getWechatpaySerial(); @PostMapping(value = "/payNotify") @ApiOperation(value = "微信支付/退款通知") public String payNotify(@RequestBody String notifyData, HttpServletRequest request, HttpServletResponse response) throws WxPayException { // 获取请求头 SignatureHeader signatureHeader = getSignatureHeader(response); String nonoc=request.getHeader("Wechatpay-Nonce"); String signature=request.getHeader("Wechatpay-Signature"); String timestamp=request.getHeader("Wechatpay-Timestamp"); String serial=request.getHeader("Wechatpay-Serial"); String signatureType=request.getHeader("Wechatpay-Signature-Type"); NotifyResponse responseData = GSON.fromJson(notifyData, NotifyResponse.class); String eventType = responseData.getEventType(); String requestBody=""; try { BufferedReader reader = request.getReader(); String line = ""; StringBuffer inputString = new StringBuffer(); while ( (line = reader.readLine()) != null ) { inputString.append(line); int resultType = 1; String resultMessage = ""; if(WxPayNotifyEventTypeEnum.TRANSACTION_SUCCESS.getCode().equals(eventType)){ PartnerTransactionsNotifyResult notifyResult = wxService.getEcommerceService().parsePartnerNotifyResult(notifyData, signatureHeader); PartnerTransactionsResult result = notifyResult.getResult(); // 支付通知回调 if("SUCCESS".equals(result.getTradeState())){ orderService.payBack(result); } if(inputString!=null && !"".equals(inputString)) { requestBody = inputString.toString(); } reader.close(); } catch (IOException e) { e.printStackTrace(); resultMessage = GSON.toJson(notifyResult); } else if(WxPayNotifyEventTypeEnum.REFUND_SUCCESS.getCode().equals(eventType) || WxPayNotifyEventTypeEnum.REFUND_ABNORMAL.getCode().equals(eventType) || WxPayNotifyEventTypeEnum.REFUND_CLOSED.getCode().equals(eventType)){ RefundNotifyResult result = wxService.getEcommerceService().parseRefundNotifyResult(notifyData, signatureHeader); orderService.orderRefundBack(result); resultType = 2; resultMessage = GSON.toJson(result); } // 保存支付/退款回调信息 backMessageService.saveBackMessage(resultType, resultMessage); return WxPayNotifyV3Response.success("成功"); } @PostMapping(value = "/profitSharingNotify") @ApiOperation(value = "微信分账通知") public String profitSharingNotify(@RequestBody String notifyData, HttpServletRequest request, HttpServletResponse response) throws WxPayException { ProfitSharingV3Service sharingV3Service = wxService.getProfitSharingV3Service(); // ProfitSharingNotifyResult // 获取请求头 SignatureHeader signatureHeader = getSignatureHeader(response); ProfitSharingNotifyNewResult notifyResult = getProfitSharingNotifyData(notifyData, signatureHeader); ProfitSharingNotifyResult result = notifyResult.getResult(); String eventType = notifyResult.getRawData().getEventType(); if(WxPayNotifyEventTypeEnum.PROFIT_SHARING_SUCCESS.getCode().equals(eventType)){ // 分账通知回调 orderService.profitSharingBack(result); } RequestParam requestParam = new RequestParam.Builder() .serialNumber(wechatPayCertificateSerialNumber) .nonce(nonoc) .signature(signature) .timestamp(timestamp) .body(requestBody) .build(); String resultMessage = GSON.toJson(notifyResult); // 保存记录分账回调信息 backMessageService.saveBackMessage(3, resultMessage); NotificationConfig notifiConfig = new RSAAutoCertificateConfig.Builder() .merchantId(merchantId) .privateKeyFromPath(privateKeyPath) .merchantSerialNumber(merchantSerialNumber) .apiV3Key(apiV3key) .build(); NotificationParser parser = new NotificationParser(notifiConfig); Transaction transaction = parser.parse(requestParam, Transaction.class); String tradeStateEnum=transaction.getTradeState().toString(); if(tradeStateEnum.equals("success")){ orderService.payBack(transaction); } Map map=new HashMap(); map.put("code","SUCCESS"); map.put("message","成功"); return map; return WxPayNotifyV3Response.success("成功"); } @RequestMapping(value = "/shareNotify", method = RequestMethod.POST) @@ -135,4 +125,49 @@ return null; } public ProfitSharingNotifyNewResult getProfitSharingNotifyData(String notifyData, SignatureHeader header) throws WxPayException { if (Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)) { throw new WxPayException("非法请求,头部信息验证失败"); } ProfitSharingNotifyData response = GSON.fromJson(notifyData, ProfitSharingNotifyData.class); ProfitSharingNotifyData.Resource resource = response.getResource(); String cipherText = resource.getCipherText(); String associatedData = resource.getAssociatedData(); String nonce = resource.getNonce(); String apiV3Key = this.wxService.getConfig().getApiV3Key(); try { String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key); ProfitSharingNotifyResult profitSharingResult = GSON.fromJson(result, ProfitSharingNotifyResult.class); ProfitSharingNotifyNewResult notifyResult = new ProfitSharingNotifyNewResult(); notifyResult.setRawData(response); notifyResult.setResult(profitSharingResult); return notifyResult; } catch (GeneralSecurityException | IOException e) { throw new WxPayException("解析报文异常!", e); } } private boolean verifyNotifySign(SignatureHeader header, String data) throws WxPayException { String beforeSign = String.format("%s\n%s\n%s\n", header.getTimeStamp(), header.getNonce(), data); Verifier verifier = this.wxService.getConfig().getVerifier(); if (verifier == null) { throw new WxPayException("证书检验对象为空"); } return verifier.verify(header.getSerialNo(), beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned()); } private SignatureHeader getSignatureHeader(HttpServletResponse response){ SignatureHeader signatureHeader = new SignatureHeader(); signatureHeader.setSerialNo(response.getHeader(WECHAT_PAY_SERIAL)); signatureHeader.setSigned(response.getHeader(WECHAT_PAY_SIGNATURE)); signatureHeader.setNonce(response.getHeader(WECHAT_PAY_NONCE)); signatureHeader.setTimeStamp(response.getHeader(WECHAT_PAY_TIMESTAMP)); return signatureHeader; } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/BackMessage.java
@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.activerecord.Model; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import java.util.Date; import lombok.Data; import lombok.EqualsAndHashCode; @@ -38,7 +39,7 @@ @TableField("del_flag") private String delFlag; /** * 回调类型1支付回调2退款回调 * 回调类型 1支付回调 2退款回调 3、分账 */ @TableField("result_type") private Integer resultType; @@ -47,6 +48,11 @@ */ @TableField("result_message") private String resultMessage; /** * 回调时间 */ @TableField("create_time") private Date createTime; @Override ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/OrderPayment.java
@@ -49,6 +49,12 @@ @TableField("shop_id") private Long shopId; /** * 订单二级商户号 */ @TableField("sub_mch_id") private String subMchId; /** * 订单id */ @TableField("order_id") ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/OrderRefund.java
@@ -99,7 +99,21 @@ * 回调时间 */ @TableField("back_time") private Date backTime; private String backTime; /** * 微信退款订单id */ @TableField("wx_refund_id") private String wxRefundId; /** * 1、发起退款 2、已退款 */ @TableField("refund_status") private Integer refundStatus; @Override ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/PaymentMessage.java
@@ -47,7 +47,7 @@ * 发起关联id */ @TableField("send_id") private Long sendId; private String sendId; /** * 发送报文 */ ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/ProfitSharing.java
@@ -40,7 +40,7 @@ private Integer delFlag; /** * 1:PROCESSING:处理中 2:FINISHED:分账完成 2:FINISHED:分账完成 */ @TableField("share_status") private Integer shareStatus; @@ -93,7 +93,15 @@ * 完成时间 */ @TableField("finish_time") private Date finishTime; private String finishTime; /** * 微信分账/回退单号 */ @TableField("wx_order_id") private String wxOrderId; /** * 分账失败原因 1、ACCOUNT_ABNORMAL : 分账接收账户异常 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/vo/AppPlaceOrderVo.java
@@ -54,7 +54,7 @@ private String packageStr; @ApiModelProperty(value = "签名方式") private String tradeType; private String signType; @ApiModelProperty(value = "签名") private String paySign; ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/vo/ProfitSharingNotifyNewResult.java
New file @@ -0,0 +1,23 @@ package com.ruoyi.order.domain.vo; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyData; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @NoArgsConstructor public class ProfitSharingNotifyNewResult implements Serializable { private static final long serialVersionUID = -6602962275015706689L; /** * 源数据 */ private ProfitSharingNotifyData rawData; /** * 解密后的数据 */ private ProfitSharingNotifyResult result; } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/RefundStatusEnum.java
New file @@ -0,0 +1,19 @@ package com.ruoyi.order.enums; import com.ruoyi.order.enums.dict.IDict; public enum RefundStatusEnum implements IDict<Integer> { /** * 退款状态,枚举值: * SUCCESS:退款成功 * CLOSE:退款关闭 * ABNORMAL:退款异常 */ SUCCESS(2, "SUCCESS"), CLOSE(3, "CLOSE"), ABNORMAL(4, "ABNORMAL"); RefundStatusEnum(Integer code, String text){ init(code, text); } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/WxPayNotifyEventTypeEnum.java
New file @@ -0,0 +1,22 @@ package com.ruoyi.order.enums; import com.ruoyi.order.enums.dict.IDict; public enum WxPayNotifyEventTypeEnum implements IDict<String> { /** * 微信回调通知 支付成功通知的类型为 TRANSACTION.SUCCESS 分账 PROFITSHARING.SUCCESS */ TRANSACTION_SUCCESS("TRANSACTION.SUCCESS", "TRANSACTION.SUCCESS"), PROFIT_SHARING_SUCCESS("PROFITSHARING.SUCCESS", "PROFITSHARING.SUCCESS"), REFUND_SUCCESS("REFUND.SUCCESS", "REFUND.SUCCESS"), REFUND_ABNORMAL("REFUND.ABNORMAL", "REFUND.ABNORMAL"), REFUND_CLOSED("REFUND.CLOSED", "REFUND.CLOSED"); WxPayNotifyEventTypeEnum(String code, String text){ init(code, text); } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/dict/DictBean.java
New file @@ -0,0 +1,15 @@ package com.ruoyi.order.enums.dict; import lombok.Data; /** * 字典bean * 只有code和text,可用于展示下拉框 * * @author luozhan */ @Data public class DictBean<T> implements IDict<T> { private final T code; private final String text; } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/dict/IDict.java
New file @@ -0,0 +1,170 @@ package com.ruoyi.order.enums.dict; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 字典接口 * <p> * 自定义的字典枚举类实现本接口后可省略属性code和text,以及对应的get方法 * 在构造方法中只需调用init方法即可初始化 * * @author luozhan * @date 2021-12 */ public interface IDict<T> { /** * 通过code获取value * * @param clazz 枚举class * @param code code * @return text */ static <T> String getTextByCode(Class<? extends IDict<T>> clazz, T code) { return Stream.of(clazz.getEnumConstants()) .filter((IDict<T> e) -> e.getCode().equals(code)) .map(IDict::getText) .findAny().orElse(""); } /** * 通过text获取code * * @param clazz 枚举class * @param text text * @return code */ static <T> T getCodeByText(Class<? extends IDict<T>> clazz, String text) { return Stream.of(clazz.getEnumConstants()) .filter((IDict<T> e) -> e.getText().equals(text)) .map(IDict::getCode) .findAny().orElse(null); } /** * 通过code获取字典枚举实例 * * @param clazz 枚举class * @param code code * @param <T> 字典code类型 * @param <R> 枚举类型 * @return 字典枚举实例 */ @SuppressWarnings("unchecked") static <T, R extends IDict<T>> R getByCode(Class<? extends IDict<T>> clazz, T code) { return Stream.of(clazz.getEnumConstants()) .filter((IDict<T> e) -> (e.getCode().equals(code))) .map(v -> (R) v) .findAny() .orElse(null); } /** * 获取给定的字典枚举项(常用下拉框数据请求) * * @param enums 可指定需要哪些项 * @return List */ @SafeVarargs static <T, E extends IDict<T>> List<IDict<T>> getItems(E... enums) { return Stream.of(enums) .map(DictPool::getDict) .collect(Collectors.toList()); } /** * 获取所有字典枚举项,除开指定的枚举 * * @param exclude 指定排除的枚举 * @return List */ @SafeVarargs @SuppressWarnings("unchecked") static <T, E extends IDict<T>> List<IDict<T>> getItemsExclude(E... exclude) { Class<IDict<T>> clazz = (Class<IDict<T>>) exclude.getClass().getComponentType(); IDict<T>[] allEnum = clazz.getEnumConstants(); List<IDict<T>> excludeList = Arrays.asList(exclude); return Stream.of(allEnum) .filter(e -> !excludeList.contains(e)) .map(DictPool::getDict) .collect(Collectors.toList()); } /** * 获取所有字典枚举项(常用下拉框数据请求) * 枚举值上标记@Deprecated的不会返回 * * @param clazz 字典枚举类 * @return List */ static <T> List<IDict<T>> getAll(Class<? extends IDict<T>> clazz) { Map<String, Field> fieldCache = Arrays.stream(clazz.getDeclaredFields()). filter(Field::isEnumConstant). collect(Collectors.toMap(Field::getName, Function.identity())); IDict<T>[] allEnum = clazz.getEnumConstants(); return Stream.of(allEnum) .filter(e -> !fieldCache.get(((Enum<?>) e).name()).isAnnotationPresent(Deprecated.class)) .map(DictPool::getDict) .collect(Collectors.toList()); } /** * 初始化 * * @param code 字典编码 * @param text 字典文本 */ default void init(T code, String text) { DictPool.putDict(this, code, text); } /** * 获取编码 * * @return 编码 */ default T getCode() { return DictPool.getDict(this).getCode(); } /** * 获取文本 * * @return 文本 */ default String getText() { return DictPool.getDict(this).getText(); } @SuppressWarnings("all") class DictPool { private static final Map<IDict, DictBean> DICT_MAP = new ConcurrentHashMap<>(); private static final Map<String, Class<? extends IDict>> DICT_NAME_CLASS_MAP = new ConcurrentHashMap<>(); static <T> void putDict(IDict<T> dict, T code, String text) { DICT_NAME_CLASS_MAP.put(dict.getClass().getName(), dict.getClass()); DICT_MAP.put(dict, new DictBean<>(code, text)); } public static List<IDict<Object>> getDict(String dictName) { Class<? extends IDict> aClass = DICT_NAME_CLASS_MAP.get(dictName); return IDict.getAll((Class<? extends IDict<Object>>) aClass); } static <K extends IDict<T>, T> DictBean<T> getDict(K dict) { return DICT_MAP.get(dict); } } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/BackMessageService.java
@@ -13,4 +13,10 @@ */ public interface BackMessageService extends IService<BackMessage> { /** * 保存回调记录 * @param resultType * @param resultMessage */ void saveBackMessage(int resultType, String resultMessage); } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/OrderPaymentService.java
@@ -3,6 +3,9 @@ import com.ruoyi.order.domain.pojo.account.OrderPayment; import com.baomidou.mybatisplus.extension.service.IService; import java.math.BigDecimal; import java.util.Date; /** * <p> * 订单支付 服务类 @@ -13,4 +16,24 @@ */ public interface OrderPaymentService extends IService<OrderPayment> { /** * 保存支付记录 * @param userId * @param shopId * @param orderId * @param payMoney * @param endTime * @param profitSharing * @param openid * @param goodsNames * @param prepayId */ void saveOrderPayment(Long userId, Long shopId, String subMchId, String orderId, BigDecimal payMoney, Date endTime, String profitSharing, String openid, String goodsNames, String prepayId); /** * * @param orderId * @return */ OrderPayment getByOrderId(String orderId); } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/PaymentMessageService.java
@@ -13,4 +13,12 @@ */ public interface PaymentMessageService extends IService<PaymentMessage> { /** * 保存请求信息 * @param sendType * @param sendId * @param sendMessage * @param resultMessage */ void savePaymentMessage(String sendType, String sendId, String sendMessage, String resultMessage); } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/ProfitSharingService.java
@@ -1,7 +1,10 @@ package com.ruoyi.order.service.account; import com.github.binarywang.wxpay.bean.ecommerce.ProfitSharingResult; import com.ruoyi.order.domain.pojo.account.ProfitSharing; import com.baomidou.mybatisplus.extension.service.IService; import java.math.BigDecimal; /** * <p> @@ -13,4 +16,12 @@ */ public interface ProfitSharingService extends IService<ProfitSharing> { /** * 创建按分账记录 * @param shopId * @param orderId * @param orderMoney * @param result */ void saveProfitSharing(Long shopId, String orderId, BigDecimal orderMoney, ProfitSharingResult result); } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/BackMessageServiceImpl.java
@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.util.Date; /** * <p> * 回调报文 服务实现类 @@ -17,4 +19,13 @@ @Service public class BackMessageServiceImpl extends ServiceImpl<BackMessageMapper, BackMessage> implements BackMessageService { @Override public void saveBackMessage(int resultType, String resultMessage) { BackMessage backMessage = new BackMessage(); backMessage.setDelFlag("0"); backMessage.setResultType(resultType); backMessage.setResultMessage(resultMessage); backMessage.setCreateTime(new Date()); this.saveOrUpdate(backMessage); } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/OrderPaymentServiceImpl.java
@@ -1,10 +1,16 @@ package com.ruoyi.order.service.impl.account; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.ruoyi.common.core.utils.uuid.IdUtils; import com.ruoyi.order.domain.pojo.account.OrderPayment; import com.ruoyi.order.mapper.account.OrderPaymentMapper; import com.ruoyi.order.service.account.OrderPaymentService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.Date; /** * <p> @@ -17,4 +23,34 @@ @Service public class OrderPaymentServiceImpl extends ServiceImpl<OrderPaymentMapper, OrderPayment> implements OrderPaymentService { @Override public void saveOrderPayment(Long userId, Long shopId, String subMchId, String orderId, BigDecimal payMoney, Date endTime, String profitSharing, String openid, String goodsNames, String prepayId) { OrderPayment payment = new OrderPayment(); payment.setPaymentId(IdUtils.simpleUUID()); payment.setUserId(userId); payment.setShopId(shopId); payment.setOrderId(orderId); payment.setPayMoney(payMoney); payment.setTimeExpire(endTime); payment.setSubMchId(subMchId); // 0 否 1、是 payment.setProfitSharing("Y".equals(profitSharing) ? 1 : 0); payment.setUserOpenId(openid); payment.setGoodsNames(goodsNames); payment.setPrepayId(prepayId); payment.setCreateTime(new Date()); payment.setDelFlag(0); this.saveOrUpdate(payment); } @Override public OrderPayment getByOrderId(String orderId) { LambdaQueryWrapper<OrderPayment> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(OrderPayment::getOrderId, orderId) .eq(OrderPayment::getDelFlag, 0) .eq(OrderPayment::getPayStatus, 2) .last(" limit 1 "); return this.getOne(queryWrapper); } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/PaymentMessageServiceImpl.java
@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.util.Date; /** * <p> * 支付报文 服务实现类 @@ -17,4 +19,15 @@ @Service public class PaymentMessageServiceImpl extends ServiceImpl<PaymentMessageMapper, PaymentMessage> implements PaymentMessageService { @Override public void savePaymentMessage(String sendType, String sendId, String sendMessage, String resultMessage) { PaymentMessage paymentMessage = new PaymentMessage(); paymentMessage.setDelFlag("0"); paymentMessage.setSendType(sendType); paymentMessage.setSendId(sendId); paymentMessage.setSendMessage(sendMessage); paymentMessage.setResultMessage(resultMessage); paymentMessage.setCreateTime(new Date()); this.saveOrUpdate(paymentMessage); } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/ProfitSharingServiceImpl.java
@@ -1,10 +1,17 @@ package com.ruoyi.order.service.impl.account; import com.github.binarywang.wxpay.bean.ecommerce.ProfitSharingResult; import com.ruoyi.common.core.utils.uuid.IdUtils; import com.ruoyi.order.domain.pojo.account.ProfitSharing; import com.ruoyi.order.mapper.account.ProfitSharingMapper; import com.ruoyi.order.service.account.ProfitSharingService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Date; import java.util.List; /** * <p> @@ -17,4 +24,31 @@ @Service public class ProfitSharingServiceImpl extends ServiceImpl<ProfitSharingMapper, ProfitSharing> implements ProfitSharingService { @Override public void saveProfitSharing(Long shopId, String orderId, BigDecimal orderMoney, ProfitSharingResult result) { ProfitSharing profitSharing = new ProfitSharing(); profitSharing.setShareId(result.getOutOrderNo()); profitSharing.setDelFlag(0); profitSharing.setShareStatus("FINISHED".equals(result.getStatus()) ? 2 : 1); profitSharing.setShopId(shopId); profitSharing.setOrderId(orderId); profitSharing.setOrderMoney(orderMoney); List<ProfitSharingResult.Receiver> receivers = result.getReceivers(); if(null != receivers && !receivers.isEmpty()){ ProfitSharingResult.Receiver receiver = receivers.get(0); profitSharing.setReceiverAccount(receiver.getReceiverMchid()); BigDecimal receiverAmount = new BigDecimal(receiver.getAmount()); receiverAmount = receiverAmount.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); profitSharing.setReceiverAmount(receiverAmount); profitSharing.setSurpMoney(orderMoney.subtract(receiverAmount)); profitSharing.setReceiverDescription(receiver.getDescription()); profitSharing.setFinishFlag(0); profitSharing.setFailReason(receiver.getFailReason()); profitSharing.setDetailId(receiver.getDetailId()); } profitSharing.setCreateTime(new Date()); this.saveOrUpdate(profitSharing); } } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/order/OrderServiceImpl.java
@@ -1,10 +1,21 @@ package com.ruoyi.order.service.impl.order; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.bean.ecommerce.ProfitSharingRequest.Receiver; import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.google.common.base.Joiner; import com.google.gson.Gson; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.DateUtils; import com.ruoyi.common.core.utils.StringUtils; @@ -12,15 +23,23 @@ import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.common.security.utils.CodeFactoryUtil; import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.order.config.WxPayConfiguration; import com.ruoyi.order.domain.dto.*; import com.ruoyi.order.domain.pojo.account.OrderPayment; import com.ruoyi.order.domain.pojo.account.OrderRefund; import com.ruoyi.order.domain.pojo.account.ProfitSharing; import com.ruoyi.order.domain.pojo.order.ConsumerGoods; import com.ruoyi.order.domain.pojo.order.Order; import com.ruoyi.order.domain.pojo.order.OrderGoods; import com.ruoyi.order.domain.pojo.order.PayRecord; import com.ruoyi.order.domain.vo.*; import com.ruoyi.order.enums.RefundStatusEnum; import com.ruoyi.order.enums.dict.IDict; import com.ruoyi.order.mapper.order.OrderMapper; import com.ruoyi.order.service.account.OrderPaymentService; import com.ruoyi.order.service.account.OrderRefundService; import com.ruoyi.order.service.account.PaymentMessageService; import com.ruoyi.order.service.account.ProfitSharingService; import com.ruoyi.order.service.order.*; import com.ruoyi.system.api.constant.AppErrorConstant; import com.ruoyi.system.api.constant.DelayTaskEnum; @@ -35,10 +54,11 @@ import com.ruoyi.system.api.domain.poji.member.MemberCoupon; import com.ruoyi.system.api.domain.poji.member.MemberGiftRecord; import com.ruoyi.system.api.domain.poji.shop.Shop; import com.ruoyi.system.api.domain.poji.shop.ShopProportionVo; import com.ruoyi.system.api.domain.poji.sys.SysUser; import com.ruoyi.system.api.domain.vo.*; import com.ruoyi.system.api.service.*; import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; @@ -47,6 +67,7 @@ import javax.annotation.Resource; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; @@ -63,6 +84,7 @@ * @since 2023-04-25 */ @Service @Slf4j public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService { @Resource @@ -110,22 +132,20 @@ @Autowired private RedissonClient redissonClient; /** 商户号 */ public static String merchantId = ""; /** 商户API私钥路径 */ public static String privateKeyPath = ""; /** 商户证书序列号 */ public static String merchantSerialNumber = ""; /** 微信支付平台证书路径 */ public static String wechatPayCertificatePath = ""; /** 微信支付 APIv3 密钥 */ public static String apiV3Key = ""; /** 微信支付APPID */ public static String appId=""; /** 微信支付商户ID */ public static String mchId=""; /** 通知地址 */ public static String notifyUrl=""; @Autowired private WxPayService wxService; @Resource private PaymentMessageService paymentMessageService; @Resource private OrderPaymentService orderPaymentService; @Resource private ProfitSharingService profitSharingService; @Resource private OrderRefundService orderRefundService; /** * @description: buyGoods @@ -153,8 +173,8 @@ Map<String, Goods> goodsMap = goodsList.stream() .collect(Collectors.toMap(Goods::getGoodsId, Function.identity())); Boolean haveDeposit = false; for(Goods goods : goodsList){ if(goods.getSubscription()!=null&&goods.getSubscription().compareTo(BigDecimal.ZERO)>0){ for (Goods goods : goodsList) { if (goods.getSubscription() != null && goods.getSubscription().compareTo(BigDecimal.ZERO) > 0) { haveDeposit = true; } } @@ -174,7 +194,7 @@ // 将优惠券列表转换为Map,以优惠券ID为键 Map<String, AppMemberCouponVo> appMemberCouponVoMap = new HashMap<>(); if(appMemberCouponVoList!=null&&!appMemberCouponVoList.isEmpty()){ if (appMemberCouponVoList != null && !appMemberCouponVoList.isEmpty()) { appMemberCouponVoMap = appMemberCouponVoList.stream().collect(Collectors.toMap(AppMemberCouponVo::getMemberCouponId, Function.identity())); } AppMemberCouponVo appMemberCouponVo; @@ -201,7 +221,7 @@ memberCouponId = appBuyGoodsDto.getMemberCouponId(); // 获取商品信息 goods = goodsMap.get(goodsId); if(goods.getGoodsStatus()!=1){ if (goods.getGoodsStatus() != 1) { throw new ServiceException(AppErrorConstant.GOODS_DOWN); } // 设置订单商品信息 @@ -234,7 +254,7 @@ if (couponType == 1 && appMemberCouponVo.getMoneyThreshold() != null && appMemberCouponVo.getDiscountMoney() != null) { moneyThreshold = appMemberCouponVo.getMoneyThreshold(); discountMoney = appMemberCouponVo.getDiscountMoney(); if (goodsTotalPrice.compareTo(moneyThreshold) >= 0 && goodsTotalPrice.compareTo(discountMoney)>0) { if (goodsTotalPrice.compareTo(moneyThreshold) >= 0 && goodsTotalPrice.compareTo(discountMoney) > 0) { goodsRealPrice = goodsTotalPrice.subtract(discountMoney); useCoupon = 1; userCouponIdSet.add(memberCouponId); @@ -242,16 +262,16 @@ } } else if (couponType == 2 && appMemberCouponVo.getDiscountPercent() != null) { discountPercent = appMemberCouponVo.getDiscountPercent(); goodsRealPrice = goodsTotalPrice.multiply(discountPercent).divide(BigDecimal.TEN,2,BigDecimal.ROUND_HALF_UP); goodsRealPrice = goodsTotalPrice.multiply(discountPercent).divide(BigDecimal.TEN, 2, BigDecimal.ROUND_HALF_UP); discountMoney = goodsTotalPrice.subtract(goodsRealPrice); useCoupon = 1; userCouponIdSet.add(memberCouponId); appSureOrderGoodsVo.setMemberCouponId(memberCouponId); } else if (couponType == 3 && appMemberCouponVo.getDiscountMoney() != null) { discountMoney = appMemberCouponVo.getDiscountMoney(); if(goodsTotalPrice.compareTo(discountMoney)>0){ if (goodsTotalPrice.compareTo(discountMoney) > 0) { goodsRealPrice = goodsTotalPrice.subtract(discountMoney); if(goodsRealPrice.compareTo(BigDecimal.ZERO)<0){ if (goodsRealPrice.compareTo(BigDecimal.ZERO) < 0) { goodsRealPrice = BigDecimal.ZERO; } useCoupon = 1; @@ -267,12 +287,12 @@ appSureOrderGoodsVo.setGoodsRealPrice(goodsRealPrice); // 获取商品押金 goodsDeposit = goods.getSubscription(); if (goodsDeposit != null && goodsDeposit.compareTo(BigDecimal.ZERO)>0) { if (goodsDeposit != null && goodsDeposit.compareTo(BigDecimal.ZERO) > 0) { goodsDeposit = goodsDeposit.multiply(buyNumBig); }else{ if(haveDeposit){ } else { if (haveDeposit) { goodsDeposit = goodsRealPrice; }else{ } else { goodsDeposit = new BigDecimal("0.00"); } } @@ -287,9 +307,9 @@ //处理优惠券列表 if (appMemberCouponVoList != null && !appMemberCouponVoList.isEmpty()) { for (AppMemberCouponVo entity : appMemberCouponVoList) { if(userCouponIdSet!=null&&userCouponIdSet.contains(entity.getMemberCouponId())){ if (userCouponIdSet != null && userCouponIdSet.contains(entity.getMemberCouponId())) { entity.setUseFlag(1); }else{ } else { entity.setUseFlag(0); } @@ -297,18 +317,18 @@ } // 过滤商品优惠券列表 List<AppMemberCouponVo> appGoodsMemberCouponVoList; for(AppSureOrderGoodsVo sureOrderGoodsVo : appSureOrderGoodsVoList) { for (AppSureOrderGoodsVo sureOrderGoodsVo : appSureOrderGoodsVoList) { appGoodsMemberCouponVoList = new ArrayList<>(); for(AppMemberCouponVo entity : appMemberCouponVoList){ if(StringUtils.isBlank(entity.getRelGoodsIds())||entity.getRelGoodsIds().contains(sureOrderGoodsVo.getGoodsId())){ if(entity.getCouponType()==1){ if(entity.getMoneyThreshold().compareTo(sureOrderGoodsVo.getGoodsTotalPrice())<0&&entity.getDiscountMoney().compareTo(sureOrderGoodsVo.getGoodsTotalPrice())<0){ for (AppMemberCouponVo entity : appMemberCouponVoList) { if (StringUtils.isBlank(entity.getRelGoodsIds()) || entity.getRelGoodsIds().contains(sureOrderGoodsVo.getGoodsId())) { if (entity.getCouponType() == 1) { if (entity.getMoneyThreshold().compareTo(sureOrderGoodsVo.getGoodsTotalPrice()) < 0 && entity.getDiscountMoney().compareTo(sureOrderGoodsVo.getGoodsTotalPrice()) < 0) { appGoodsMemberCouponVoList.add(entity); } }else if(entity.getCouponType()==2){ } else if (entity.getCouponType() == 2) { appGoodsMemberCouponVoList.add(entity); }else if(entity.getCouponType()==3){ if(entity.getDiscountMoney().compareTo(sureOrderGoodsVo.getGoodsTotalPrice())<0){ } else if (entity.getCouponType() == 3) { if (entity.getDiscountMoney().compareTo(sureOrderGoodsVo.getGoodsTotalPrice()) < 0) { appGoodsMemberCouponVoList.add(entity); } } @@ -415,8 +435,8 @@ if (activityGoodsGetVo.getActivityStatus() == 2) { throw new ServiceException(AppErrorConstant.ACTIVITY_END); } Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+activityId+"-"+goodsId); if(surpNum==null||surpNum<1){ Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + activityId + "-" + goodsId); if (surpNum == null || surpNum < 1) { throw new ServiceException(AppErrorConstant.ACTIVITY_GOODS_NULL); } if (activityGoodsGetVo.getAvailableBuyNum() < 1) { @@ -426,7 +446,7 @@ throw new ServiceException(AppErrorConstant.ACTIVITY_GOODS_BEYOND); } Goods goods = remoteGoodsService.getGoods(goodsId).getData(); if(goods.getGoodsStatus()!=1){ if (goods.getGoodsStatus() != 1) { throw new ServiceException(AppErrorConstant.GOODS_DOWN); } appPanicBuyVo.setActivityId(activityId); @@ -456,9 +476,9 @@ appPanicBuyVo.setGoodsTotalPrice(goodsTotalPrice); appPanicBuyVo.setGoodsRealPrice(goodsRealPrice); goodsDeposit = activityGoodsGetVo.getActivitySubscription(); if (goodsDeposit == null || goodsDeposit.compareTo(BigDecimal.ZERO)==0) { if (goodsDeposit == null || goodsDeposit.compareTo(BigDecimal.ZERO) == 0) { goodsDeposit = new BigDecimal("0.00"); }else{ } else { goodsDeposit = goodsDeposit.multiply(buyNumBig); } appPanicBuyVo.setGoodsDeposit(goodsDeposit); @@ -498,8 +518,8 @@ Map<String, Goods> goodsMap = goodsList.stream() .collect(Collectors.toMap(Goods::getGoodsId, Function.identity())); Boolean haveDeposit = false; for(Goods goods : goodsList){ if(goods.getSubscription()!=null&&goods.getSubscription().compareTo(BigDecimal.ZERO)>0){ for (Goods goods : goodsList) { if (goods.getSubscription() != null && goods.getSubscription().compareTo(BigDecimal.ZERO) > 0) { haveDeposit = true; } } @@ -510,7 +530,7 @@ Integer serviceNum; Integer goodsNum = 0; String memberCouponId; Goods goods; Goods goods = null; GoodsFile goodsFile; BigDecimal goodsPrice; BigDecimal goodsTotalPrice; @@ -542,6 +562,7 @@ HashSet<String> goodsTypeSet = new HashSet<>(); //使用优惠券 StringJoiner memberCouponSJ = new StringJoiner(","); List<String> goodsNameList = new ArrayList<>(); // 遍历购买的商品列表 for (AppBuyGoodsDto appBuyGoodsDto : appBuyGoodsDtoList) { appSureOrderGoodsVo = new AppSureOrderGoodsVo(); @@ -551,6 +572,7 @@ memberCouponId = appBuyGoodsDto.getMemberCouponId(); // 获取商品信息 goods = goodsMap.get(goodsId); goodsNameList.add(goods.getGoodsName()); // 设置商品信息 appSureOrderGoodsVo.setGoodsId(goodsId); appSureOrderGoodsVo.setGoodsName(goods.getGoodsName()); @@ -596,7 +618,7 @@ } } else if (couponType == 2 && appMemberCouponVo.getDiscountPercent() != null) { discountPercent = appMemberCouponVo.getDiscountPercent(); goodsRealPrice = goodsTotalPrice.multiply(discountPercent).divide(BigDecimal.TEN,2, BigDecimal.ROUND_HALF_UP); goodsRealPrice = goodsTotalPrice.multiply(discountPercent).divide(BigDecimal.TEN, 2, BigDecimal.ROUND_HALF_UP); discountMoney = goodsTotalPrice.subtract(goodsRealPrice); useCoupon = 1; } else if (couponType == 3 && appMemberCouponVo.getDiscountMoney() != null) { @@ -613,12 +635,12 @@ appSureOrderGoodsVo.setGoodsRealPrice(goodsRealPrice); // 获取商品押金 goodsDeposit = goods.getSubscription(); if (goodsDeposit != null && goodsDeposit.compareTo(BigDecimal.ZERO)>0) { if (goodsDeposit != null && goodsDeposit.compareTo(BigDecimal.ZERO) > 0) { goodsDeposit = goodsDeposit.multiply(buyNumBig); }else{ if(haveDeposit){ } else { if (haveDeposit) { goodsDeposit = goodsRealPrice; }else{ } else { goodsDeposit = new BigDecimal("0.00"); } } @@ -714,53 +736,127 @@ } appPlaceOrderVo.setOrderId(orderId); appPlaceOrderVo.setOrderNo(orderNo); // 调用支付 /* CombineTransactionsJsRequest request=new CombineTransactionsJsRequest(); request.setCombineAppid(); request.setCombineMchid(); profitsSharingService.combineTransactions(request);*/ //构建预下单支付对象 /*PrepayRequest request = new PrepayRequest(); Amount amount = new Amount(); amount.setTotal(Integer.parseInt(order.getPayMoney().multiply(new BigDecimal(100)).toString())); request.setAmount(amount); request.setAppid(this.appId); request.setMchid(this.mchId); request.setDescription(order.getGoodsInfo()); request.setNotifyUrl(this.notifyUrl); request.setOutTradeNo(order.getOrderId()); Payer payer = new Payer(); payer.setOpenid("oLTPCuN5a-nBD4rAL_fa********"); request.setPayer(payer);*/ //返回前端唤醒支付结果信息 /*PrepayWithRequestPaymentResponse res = this.getJsApIResponse(request); appPlaceOrderVo.setAppId(this.appId); appPlaceOrderVo.setTimeStamp(res.getTimeStamp()); appPlaceOrderVo.setNonceStr(res.getNonceStr()); appPlaceOrderVo.setPackageStr(res.getPackageVal()); appPlaceOrderVo.setTradeType(res.getSignType()); appPlaceOrderVo.setPaySign(res.getPaySign());*/ String goodsName = ""; if (null != goods) { goodsName = goods.getGoodsName(); } // 小程序微信下单支付 createWxPayInfo(appPlaceOrderVo, userId, order.getShopId(), goodsName, orderNo, orderId, order.getPayMoney(), appPlaceOrderDto.getOpenid(), appPlaceOrderDto.getSpbillCreateIp(), goodsNameList); //减去优惠券 if(memberCouponSJ!=null&&StringUtils.isNotBlank(memberCouponSJ.toString())){ if (StringUtils.isNotBlank(memberCouponSJ.toString())) { remoteCouponService.useMemberCoupon(memberCouponSJ.toString()); } //生成自动取消订单延时任务 Integer delayTime = 30; if(redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)){ if (redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)) { delayTime = redisService.getCacheObject(SecurityConstant.AUTO_CANCEL_ORDER_TIME); } redisService.setCacheObject(DelayTaskEnum.ORDER_AUTOMATIC_CANCEL.getCode() + "-" + orderId, orderId, delayTime.longValue(), TimeUnit.MINUTES); DelayTask delayTask = new DelayTask(); delayTask.setDelFlag(0); delayTask.setCreateTime(new Date()); delayTask.setEndTime(DateUtils.addMinutes(new Date(),delayTime)); delayTask.setEndTime(DateUtils.addMinutes(new Date(), delayTime)); delayTask.setRedisKey(DelayTaskEnum.ORDER_AUTOMATIC_CANCEL.getCode() + "-" + orderId); remoteConfigService.addDelayTask(delayTask); appPlaceOrderVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(),delayTime)); appPlaceOrderVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(), delayTime)); return appPlaceOrderVo; } /** * 小程序支付下单API * @param appPlaceOrderVo * @param userId * @param shopId * @param goodsName * @param orderNo * @param orderId * @param payMoney * @param openid * @param payerClientIp * @param goodsNameList */ private void createWxPayInfo(AppPlaceOrderVo appPlaceOrderVo, Long userId, Long shopId, String goodsName, String orderNo, String orderId, BigDecimal payMoney, String openid, String payerClientIp, List<String> goodsNameList){ try { // 创建支付订单 R<String> resultMch = remoteShopService.getShopSubMchId(shopId); String subMchId = resultMch.getData(); if (StringUtils.isEmpty(subMchId)) { throw new ServiceException("获取微信商户号失败"); } WxPayConfig config = wxService.getConfig(); config.setSubMchId(subMchId); PartnerTransactionsRequest request = new PartnerTransactionsRequest(); request.setSpAppid(config.getAppId()); request.setSpMchid(config.getMchId()); request.setSubMchid(config.getSubMchId()); // 商品描述 body String description = goodsName + "-商品购买"; request.setDescription(description); request.setOutTradeNo(orderNo); request.setNotifyUrl(config.getNotifyUrl()); // 结算信息 PartnerTransactionsRequest.SettleInfo settleInfo = new PartnerTransactionsRequest.SettleInfo(); settleInfo.setProfitSharing(true); settleInfo.setSubsidyAmount(BigDecimal.ZERO); request.setSettleInfo(settleInfo); // 订单总金额 Integer totalFee = payMoney.multiply(new BigDecimal(100)).intValue(); PartnerTransactionsRequest.Amount amount = new PartnerTransactionsRequest.Amount(); amount.setTotal(totalFee); amount.setCurrency("CNY"); request.setAmount(amount); // 支付者 PartnerTransactionsRequest.Payer payer = new PartnerTransactionsRequest.Payer(); payer.setSpOpenid(openid); request.setPayer(payer); // 场景信息 PartnerTransactionsRequest.SceneInfo sceneInfo = new PartnerTransactionsRequest.SceneInfo(); sceneInfo.setPayerClientIp(payerClientIp); request.setSceneInfo(sceneInfo); TransactionsResult.JsapiResult result = wxService.getEcommerceService().partnerTransactions(TradeTypeEnum.JSAPI, request); // 请求参数 Gson gson = new Gson(); String payRequestJson = gson.toJson(request); // 返回参数 String payResponseJson = gson.toJson(result); // 支付相关信息返回 appPlaceOrderVo.setAppId(result.getAppId()); appPlaceOrderVo.setMchId(config.getMchId()); appPlaceOrderVo.setTimeStamp(result.getTimeStamp()); appPlaceOrderVo.setNonceStr(result.getNonceStr()); appPlaceOrderVo.setPackageStr(result.getPackageValue()); appPlaceOrderVo.setSignType(result.getSignType()); appPlaceOrderVo.setPaySign(result.getPaySign()); appPlaceOrderVo.setPrepayId(result.getPackageValue()); // 保存支付订单统一下单日志 paymentMessageService.savePaymentMessage("1", orderId, payRequestJson, payResponseJson); // 保存支付订单统一下单支付记录 orderPaymentService.saveOrderPayment(userId, shopId, subMchId, orderId, payMoney, appPlaceOrderVo.getEndTime(), "Y", openid, Joiner.on(";").join(goodsNameList), result.getPackageValue()); } catch (WxPayException e) { throw new ServiceException(e.getMessage()); } } @@ -784,17 +880,17 @@ String goodsId = appPlaceActivityDto.getGoodsId(); Integer buyNum = appPlaceActivityDto.getBuyNum(); //redis库存控制极简版 String goodsLock = "goods_lock_"+activityId+"_"+goodsId; String goodsLock = "goods_lock_" + activityId + "_" + goodsId; RLock redissonLock = redissonClient.getLock(goodsLock); try{ try { redissonLock.lock(30, TimeUnit.SECONDS); Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+activityId+"-"+goodsId); if(surpNum==null||surpNum<buyNum){ Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + activityId + "-" + goodsId); if (surpNum == null || surpNum < buyNum) { throw new ServiceException(AppErrorConstant.ACTIVITY_GOODS_NULL); }else{ redisService.setCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+activityId+"-"+goodsId,surpNum-buyNum); } else { redisService.setCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + activityId + "-" + goodsId, surpNum - buyNum); } }finally{ } finally { redissonLock.unlock(); } // 定义购买数量,服务数量,商品数量 @@ -874,9 +970,9 @@ appSureOrderGoodsVo.setGoodsRealPrice(goodsRealPrice); // 获取商品押金 goodsDeposit = activityGoodsGetVo.getActivitySubscription(); if (goodsDeposit == null || goodsDeposit.compareTo(BigDecimal.ZERO)==0) { if (goodsDeposit == null || goodsDeposit.compareTo(BigDecimal.ZERO) == 0) { goodsDeposit = new BigDecimal("0.00"); }else{ } else { goodsDeposit = goodsDeposit.multiply(buyNumBig); } appSureOrderGoodsVo.setGoodsDeposit(goodsDeposit); @@ -923,9 +1019,9 @@ goodsTypeSet.add("单品"); } String goodsNurse = goods.getGoodsNurses(); if(StringUtils.isNotBlank(goodsNurse)){ if (StringUtils.isNotBlank(goodsNurse)) { String[] goodsNurseArr = goodsNurse.split(","); for(String str : goodsNurseArr){ for (String str : goodsNurseArr) { goodsNurseSet.add(str); } } @@ -969,7 +1065,7 @@ // 保存订单 this.save(order); // 更新用户商品类型 if (goodsTypeSet != null || goodsNurseSet!= null) { if (goodsTypeSet != null || goodsNurseSet != null) { AppMemberGoodsTypeDto appMemberGoodsTypeDto = new AppMemberGoodsTypeDto(); appMemberGoodsTypeDto.setUserId(userId); appMemberGoodsTypeDto.setGoodsTypeSet(goodsTypeSet); @@ -1037,17 +1133,17 @@ remoteActivityService.addActivityRecord(activityRecord); //生成自动取消订单延时任务 Integer delayTime = 30; if(redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)){ if (redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)) { delayTime = redisService.getCacheObject(SecurityConstant.AUTO_CANCEL_ORDER_TIME); } redisService.setCacheObject(DelayTaskEnum.ORDER_AUTOMATIC_CANCEL.getCode() + "-" + orderId, orderId, delayTime.longValue(), TimeUnit.MINUTES); DelayTask delayTask = new DelayTask(); delayTask.setDelFlag(0); delayTask.setCreateTime(new Date()); delayTask.setEndTime(DateUtils.addMinutes(new Date(),delayTime)); delayTask.setEndTime(DateUtils.addMinutes(new Date(), delayTime)); delayTask.setRedisKey(DelayTaskEnum.ORDER_AUTOMATIC_CANCEL.getCode() + "-" + orderId); remoteConfigService.addDelayTask(delayTask); appPlaceOrderVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(),delayTime)); appPlaceOrderVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(), delayTime)); return appPlaceOrderVo; } @@ -1076,7 +1172,7 @@ shopMap.put(shopIdLong, shopTemp); }); Integer delayTime = 30; if(redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)){ if (redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)) { delayTime = redisService.getCacheObject(SecurityConstant.AUTO_CANCEL_ORDER_TIME); } for (AppUserOrderPageVo appUserOrderPageVo : appUserOrderPageVoList) { @@ -1089,7 +1185,7 @@ appUserOrderPageVo.setSameShop(0); } appUserOrderPageVo.setVerifyCode("1-" + appUserOrderPageVo.getOrderId()); appUserOrderPageVo.setEndTime(DateUtils.addMinutes(appUserOrderPageVo.getCreateTime(),delayTime)); appUserOrderPageVo.setEndTime(DateUtils.addMinutes(appUserOrderPageVo.getCreateTime(), delayTime)); } } return appUserOrderPageVoList; @@ -1130,10 +1226,10 @@ appUserOrderGetVo.setVerifyCode("1-" + orderId); //生成自动取消订单延时任务 Integer delayTime = 30; if(redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)){ if (redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)) { delayTime = redisService.getCacheObject(SecurityConstant.AUTO_CANCEL_ORDER_TIME); } appUserOrderGetVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(),delayTime)); appUserOrderGetVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(), delayTime)); return appUserOrderGetVo; } @@ -1145,13 +1241,15 @@ @Override public void cancelOrder(String orderId) { Order order = this.getById(orderId); // 订单状态-1.删除 0.已取消 1.待支付 2.待核销 3.已完成 if (order.getOrderStatus() == 0) { throw new ServiceException(AppErrorConstant.CANCEL_CANCEL_ORDER); }else if (order.getOrderStatus() == 1) { } else if (order.getOrderStatus() == 1) { this.autoCancelOrder(orderId); }else if(order.getOrderStatus() == 2){ } else if (order.getOrderStatus() == 2) { // 2.待核销 -> 订单退款 this.refundOrder(orderId); }else{ } else { throw new ServiceException(AppErrorConstant.CANCEL_ERROR_ORDER); } } @@ -1267,7 +1365,7 @@ } ShopRelUserVo shopRelUserVo = remoteShopService.getShopByUserId(merVerifyOrderDto.getUserId()).getData(); if(!shopRelUserVo.getShopId().equals(order.getShopId())){ if (!shopRelUserVo.getShopId().equals(order.getShopId())) { throw new ServiceException(AppErrorConstant.VERIFY_SHOP_ERROR); } order.setOrderStatus(3); @@ -1277,9 +1375,9 @@ order.setOffPayTime(nowTime); order.setPayMoney(order.getPayMoney().add(relPayMoney)); order.setChangeReceivableMoney(relReceiveMoney); if(order.getPayMoney().compareTo(order.getChangeReceivableMoney())>=0){ if (order.getPayMoney().compareTo(order.getChangeReceivableMoney()) >= 0) { order.setCloseFlag(1); }else{ } else { order.setCloseFlag(0); } this.saveOrUpdate(order); @@ -1299,7 +1397,7 @@ List<ConsumerGoods> consumerGoodsList = new ArrayList<>(); for (AppUserOrderGoodsPageVo appUserOrderGoodsPageVo : appUserOrderGoodsPageVoList) { Integer buyNum = appUserOrderGoodsPageVo.getBuyNum(); if(appUserOrderGoodsPageVo.getCycleNumFlag()==0){ if (appUserOrderGoodsPageVo.getCycleNumFlag() == 0) { consumerGoods = new ConsumerGoods(); goods = goodsMap.get(appUserOrderGoodsPageVo.getGoodsId()); consumerGoodsId = IdUtils.simpleUUID(); @@ -1324,8 +1422,8 @@ consumerGoods.setSourceFrom(1); consumerGoods.setSureNum(buyNum); consumerGoodsList.add(consumerGoods); }else{ for(int i=0;i<buyNum;i++){ } else { for (int i = 0; i < buyNum; i++) { consumerGoods = new ConsumerGoods(); goods = goodsMap.get(appUserOrderGoodsPageVo.getGoodsId()); consumerGoodsId = IdUtils.simpleUUID(); @@ -1386,9 +1484,8 @@ merVerifyOrderVo.setUserMobile(member.getMobile()); merVerifyOrderVo.setCloseFlag(order.getCloseFlag()); // if(order.getOrderFrom()==2){ //profitsSharingService.applyProfitSharing(); if (order.getOrderFrom() == 2) { // profitsSharingService.applyProfitSharing(); } //创建支付记录 @@ -1399,8 +1496,87 @@ payRecord.setPayTime(new Date()); payRecord.setPayType(merVerifyOrderDto.getPayType()); payRecordService.save(payRecord); return merVerifyOrderVo; submitProfitSharing(orderId, order.getOrderNo(), order.getShopId(), order.getOrderMoney()); return merVerifyOrderVo; } private void submitProfitSharing(String orderId, String orderNo, Long shopId, BigDecimal orderMoney) { String sendMessage = ""; String resultMessage = ""; try { // 核销完成 开始分账(平台收取服务费) OrderPayment payment = orderPaymentService.getByOrderId(orderId); String transactionId = payment.getTransactionId(); // 平台特约商户号,用户平台收取服务费 String platformTyMacId = WxPayConfiguration.getPlatformTyMacId(); R<String> resultMch = remoteShopService.getShopSubMchId(shopId); String subMchId = resultMch.getData(); if (!StringUtils.isEmpty(subMchId)) { R<ShopProportionVo> resultShopProportion = remoteShopService.getShopProportion(shopId); ShopProportionVo shopProportion = resultShopProportion.getData(); if (null != shopProportion) { ProfitSharingRequest request = new ProfitSharingRequest(); request.setSubMchid(subMchId); request.setTransactionId(transactionId); request.setOutOrderNo(orderNo); List<Receiver> receiverList = new ArrayList<>(); String description = "订单:" + orderNo + " 平台抽取佣金"; BigDecimal proportionPercent = shopProportion.getProportionPercent(); if (null == proportionPercent) { proportionPercent = BigDecimal.ZERO; } ProfitSharingResult result = new ProfitSharingResult(); result.setOutOrderNo(orderNo); result.setStatus("FINISHED"); // 计算分成金额 int amount = orderMoney.multiply(proportionPercent).setScale(0, RoundingMode.UP).intValue(); log.info("订单分账:{} 分账金额: {}", orderNo, amount); if (amount > 0) { // 分账创建 Receiver receiver = new Receiver(); receiver.setType("MERCHANT_ID"); receiver.setReceiverAccount(platformTyMacId); receiver.setAmount(amount); receiver.setDescription(description); receiverList.add(receiver); request.setReceivers(receiverList); // 分账完成 request.setFinish(true); result = wxService.getEcommerceService().profitSharing(request); } // 创建分账记录 profitSharingService.saveProfitSharing(shopId, orderId, orderMoney, result); // 保存请求信息 sendMessage = JSONObject.toJSONString(request); resultMessage = JSONObject.toJSONString(result); } else { resultMessage = String.format("订单分账:%s 获取商户分成失败", orderNo); log.info(resultMessage); } } else { resultMessage = String.format("订单分账:%s 获取微信商户号失败", orderNo); log.info(resultMessage); } } catch (WxPayException e) { resultMessage = String.format("订单分账:%s 分账失败:%s", orderNo, e.getMessage()); log.info(resultMessage); e.printStackTrace(); } // 保存分账信息 paymentMessageService.savePaymentMessage("4", orderId, sendMessage, resultMessage); } /** @@ -1415,7 +1591,7 @@ public MerVerifyCouponVo sureVerifyCoupon(MerVerifyCouponDto merVerifyCouponDto) { MerVerifyCouponGetVo verifyCouponGetVo = remoteCouponService.getVerifyCoupon(merVerifyCouponDto.getMemberCouponId()).getData(); ShopRelUserVo shopRelUserVo = remoteShopService.getShopByUserId(merVerifyCouponDto.getUserId()).getData(); if(!shopRelUserVo.getShopId().equals(verifyCouponGetVo.getMemberCoupon().getShopId())){ if (!shopRelUserVo.getShopId().equals(verifyCouponGetVo.getMemberCoupon().getShopId())) { throw new ServiceException(AppErrorConstant.VERIFY_SHOP_ERROR); } // 创建MerVerifyCouponVo对象 @@ -1478,7 +1654,7 @@ appShopGoodsGetDto.setShopId(memberCoupon.getShopId()); //获取商户服务次数 shopGoods = remoteGoodsService.getShopGoods(appShopGoodsGetDto).getData(); if(shopGoods!=null){ if (shopGoods != null) { consumerGoods.setServiceNum(shopGoods.getServiceNum()); } consumerGoods.setUsedNum(0); @@ -1499,36 +1675,36 @@ /** * @description 确认核销奖品 * @author jqs * @date 2023/7/9 9:54 * @param merVerifyPrizeDto * @return MerVerifyAwardVo * @return MerVerifyAwardVo * @description 确认核销奖品 * @author jqs * @date 2023/7/9 9:54 */ @Override @Transactional public MerVerifyAwardVo sureVerifyPrize(MerVerifyPrizeDto merVerifyPrizeDto){ public MerVerifyAwardVo sureVerifyPrize(MerVerifyPrizeDto merVerifyPrizeDto) { MerVerifyAwardVo merVerifyAwardVo = new MerVerifyAwardVo(); MemberGiftRecord memberGiftRecord = remoteMemberService.getVerifyPrize(merVerifyPrizeDto.getPrizeId()).getData(); if(memberGiftRecord==null||memberGiftRecord.getVerifyStatus()!=1){ if (memberGiftRecord == null || memberGiftRecord.getVerifyStatus() != 1) { throw new ServiceException(AppErrorConstant.COUPON_USED); } ShopRelUserVo shopRelUserVo = remoteShopService.getShopByUserId(merVerifyPrizeDto.getUserId()).getData(); if(!shopRelUserVo.getShopId().equals(memberGiftRecord.getShopId())){ if (!shopRelUserVo.getShopId().equals(memberGiftRecord.getShopId())) { throw new ServiceException(AppErrorConstant.VERIFY_SHOP_ERROR); } Member member = remoteMemberService.getMember(memberGiftRecord.getUserId()).getData(); merVerifyAwardVo.setUserName(member.getRealName()); merVerifyAwardVo.setUserMobile(member.getMobile()); if(memberGiftRecord.getGiftFrom()==1){ if (memberGiftRecord.getGiftFrom() == 1) { merVerifyAwardVo.setGiftFrom("平台生日卡"); }else{ } else { merVerifyAwardVo.setGiftFrom("商户生日卡"); } merVerifyAwardVo.setGiftType(memberGiftRecord.getGiftType()); List<BirthdayGiftSendDto> giftSendDtoList = new ArrayList<>(); BirthdayGiftSendDto birthdayGiftSendDto = new BirthdayGiftSendDto(); switch(memberGiftRecord.getGiftType()){ switch (memberGiftRecord.getGiftType()) { case 1: merVerifyAwardVo.setGiftName(memberGiftRecord.getCouponName()); merVerifyAwardVo.setGiftNumber(Double.valueOf(memberGiftRecord.getCouponNumber())); @@ -1577,7 +1753,7 @@ merVerifyAwardVo.setCreateTime(memberGiftRecord.getCreateTime()); memberGiftRecord.setVerifyStatus(2); memberGiftRecord.setVerifyTime(new Date()); if(member.getBindingFlag()!=1){ if (member.getBindingFlag() != 1) { AppMemberBindingDto appMemberBindingDto = new AppMemberBindingDto(); appMemberBindingDto.setBindingFlag(1); appMemberBindingDto.setShopId(merVerifyPrizeDto.getShopId()); @@ -1625,9 +1801,9 @@ order.setOfflinePayMoney(order.getOfflinePayMoney().add(merCloseOrderDto.getPayMoney())); order.setOffPayTime(new Date()); order.setPayMoney(order.getPayMoney().add(merCloseOrderDto.getPayMoney())); if(order.getPayMoney().compareTo(order.getChangeReceivableMoney())>=0){ if (order.getPayMoney().compareTo(order.getChangeReceivableMoney()) >= 0) { order.setCloseFlag(0); }else{ } else { order.setCloseFlag(1); } this.saveOrUpdate(order); @@ -1758,7 +1934,7 @@ } } else if (couponType == 2 && appMemberCouponVo.getDiscountPercent() != null) { discountPercent = appMemberCouponVo.getDiscountPercent(); goodsRealPrice = goodsTotalPrice.multiply(discountPercent).divide(BigDecimal.TEN,2, BigDecimal.ROUND_HALF_UP); goodsRealPrice = goodsTotalPrice.multiply(discountPercent).divide(BigDecimal.TEN, 2, BigDecimal.ROUND_HALF_UP); discountMoney = goodsRealPrice.subtract(goodsTotalPrice); } else if (couponType == 3 && appMemberCouponVo.getDiscountMoney() != null) { discountMoney = appMemberCouponVo.getDiscountMoney(); @@ -1768,9 +1944,9 @@ } goodsDeposit = goods.getSubscription(); if (goodsDeposit == null || goodsDeposit.compareTo(BigDecimal.ZERO)==0) { if (goodsDeposit == null || goodsDeposit.compareTo(BigDecimal.ZERO) == 0) { goodsDeposit = new BigDecimal("0.00"); }else{ } else { goodsDeposit = goodsDeposit.multiply(buyNumBig); } goodsNum = goodsNum + buyNum; @@ -1850,9 +2026,9 @@ order.setOfflinePayMoney(merNewOrderDto.getPayMoney()); order.setOffPayTime(nowTime); order.setPayType(1); if(order.getPayMoney().compareTo(order.getChangeReceivableMoney())>=0){ if (order.getPayMoney().compareTo(order.getChangeReceivableMoney()) >= 0) { order.setCloseFlag(0); }else{ } else { order.setCloseFlag(1); } this.save(order); @@ -1863,7 +2039,7 @@ List<ConsumerGoods> consumerGoodsList = new ArrayList<>(); for (AppUserOrderGoodsPageVo appUserOrderGoodsPageVo : appUserOrderGoodsPageVoList) { Integer buyGoodsNum = appUserOrderGoodsPageVo.getBuyNum(); if(appUserOrderGoodsPageVo.getCycleNumFlag()==0){ if (appUserOrderGoodsPageVo.getCycleNumFlag() == 0) { consumerGoods = new ConsumerGoods(); goods = remoteGoodsService.getGoods(appUserOrderGoodsPageVo.getGoodsId()).getData(); consumerGoodsId = IdUtils.simpleUUID(); @@ -1888,8 +2064,8 @@ consumerGoods.setSourceFrom(1); consumerGoods.setSureNum(buyGoodsNum); consumerGoodsList.add(consumerGoods); }else{ for(int i=0;i<buyGoodsNum;i++) { } else { for (int i = 0; i < buyGoodsNum; i++) { consumerGoods = new ConsumerGoods(); goods = remoteGoodsService.getGoods(appUserOrderGoodsPageVo.getGoodsId()).getData(); consumerGoodsId = IdUtils.simpleUUID(); @@ -1952,11 +2128,11 @@ */ @Override public List<MerOrderPageVo> pageMerOrder(Page page, MerOrderPageDto merOrderPageDto) { if(StringUtils.isNotBlank(merOrderPageDto.getKeyword())){ if (StringUtils.isNotBlank(merOrderPageDto.getKeyword())) { MgtUserIdByKeywordDto userIdByKeywordDto = new MgtUserIdByKeywordDto(); userIdByKeywordDto.setKeyword(merOrderPageDto.getKeyword()); MgtUserIdByKeywordVo userIdByKeywordVo = remoteMemberService.getUserIdByKeyword(userIdByKeywordDto).getData(); if(StringUtils.isNotBlank(userIdByKeywordVo.getUserIds())){ if (StringUtils.isNotBlank(userIdByKeywordVo.getUserIds())) { List<Long> userIdList = Arrays.stream(userIdByKeywordVo.getUserIds().split(",")) .map(Long::parseLong) .collect(Collectors.toList()); @@ -2000,12 +2176,12 @@ Map<Long, MgtSimpleMemberVo> userMap = simpleMemberVoList.stream() .collect(Collectors.toMap(MgtSimpleMemberVo::getUserId, Function.identity())); for (MerOrderPageVo merOrderPageVo : merOrderPageVoList) { if(merOrderPageVo.getUserId()!=null&&userMap.get(merOrderPageVo.getUserId())!=null){ if (merOrderPageVo.getUserId() != null && userMap.get(merOrderPageVo.getUserId()) != null) { merOrderPageVo.setUserName(userMap.get(merOrderPageVo.getUserId()).getRealName()); merOrderPageVo.setNickName(userMap.get(merOrderPageVo.getUserId()).getNickName()); merOrderPageVo.setUserMobile(userMap.get(merOrderPageVo.getUserId()).getUserMobile()); }else{ log.debug("订单merOrderPageVo"+merOrderPageVo.getOrderId()+"缺少用户"); } else { log.debug("订单merOrderPageVo" + merOrderPageVo.getOrderId() + "缺少用户"); } } } @@ -2372,9 +2548,9 @@ mgtOrderDetailVo.setCancelTime(order.getCancelTime()); mgtOrderDetailVo.setOrderRemark(order.getOrderRemark()); mgtOrderDetailVo.setShopId(order.getShopId()); if(shop!=null){ if (shop != null) { mgtOrderDetailVo.setShopName(shop.getShopName()); }else{ } else { mgtOrderDetailVo.setShopName("商户已被删除"); } mgtOrderDetailVo.setVerifyTime(order.getUseTime()); @@ -2966,11 +3142,11 @@ } else if (couponStatus != 1) { throw new ServiceException(AppErrorConstant.COUPON_NO_FIND); } if (memberCoupon.getCouponFrom()==2&&!memberCoupon.getShopId().equals(shopId)) { if (memberCoupon.getCouponFrom() == 2 && !memberCoupon.getShopId().equals(shopId)) { throw new ServiceException(AppErrorConstant.VERIFY_SHOP_ERROR); } //如果是平台优惠券 if(memberCoupon.getCouponFrom()==1&&memberCoupon.getShopId()==null){ if (memberCoupon.getCouponFrom() == 1 && memberCoupon.getShopId() == null) { memberCoupon.setShopId(shopId); } List<Goods> goodsList = verifyCouponGetVo.getGoodsList(); @@ -3008,34 +3184,34 @@ } /** * @description 获取核销奖品 * @author jqs * @date 2023/7/8 17:46 * @param verifyCode * @param shopId * @return MerVerifyAwardVo * @return MerVerifyAwardVo * @description 获取核销奖品 * @author jqs * @date 2023/7/8 17:46 */ @Override public MerVerifyAwardVo verifyPrize(String verifyCode,Long shopId){ public MerVerifyAwardVo verifyPrize(String verifyCode, Long shopId) { MerVerifyAwardVo merVerifyAwardVo = new MerVerifyAwardVo(); MemberGiftRecord memberGiftRecord = remoteMemberService.getVerifyPrize(verifyCode).getData(); if(memberGiftRecord==null||memberGiftRecord.getVerifyStatus()!=1){ if (memberGiftRecord == null || memberGiftRecord.getVerifyStatus() != 1) { throw new ServiceException(AppErrorConstant.COUPON_USED); } if(memberGiftRecord.getGiftFrom()==2&&!memberGiftRecord.getShopId().equals(shopId)){ if (memberGiftRecord.getGiftFrom() == 2 && !memberGiftRecord.getShopId().equals(shopId)) { throw new ServiceException(AppErrorConstant.VERIFY_SHOP_ERROR); } Member member = remoteMemberService.getMember(memberGiftRecord.getUserId()).getData(); merVerifyAwardVo.setUserName(member.getRealName()); merVerifyAwardVo.setUserMobile(member.getMobile()); if(memberGiftRecord.getGiftFrom()==1){ if (memberGiftRecord.getGiftFrom() == 1) { merVerifyAwardVo.setGiftFrom("平台生日卡"); }else{ } else { merVerifyAwardVo.setGiftFrom("商户生日卡"); } merVerifyAwardVo.setGiftType(memberGiftRecord.getGiftType()); switch(memberGiftRecord.getGiftType()){ switch (memberGiftRecord.getGiftType()) { case 1: merVerifyAwardVo.setGiftName(memberGiftRecord.getCouponName()); merVerifyAwardVo.setGiftNumber(Double.valueOf(memberGiftRecord.getCouponNumber())); @@ -3175,22 +3351,22 @@ orderMoneyValue[i] = BigDecimal.ZERO; } }*/ List<MgtMapTotalPlusVo> mgtMapTotalPlusVoList = orderMapper.listMerOrderDistributionTotal(merTotalDto); if(mgtMapTotalPlusVoList!=null&&!mgtMapTotalPlusVoList.isEmpty()) { String[] orderTotalKey = new String[mgtMapTotalPlusVoList.size()]; Integer[] orderTotalValue = new Integer[mgtMapTotalPlusVoList.size()]; BigDecimal[] orderMoneyValue = new BigDecimal[mgtMapTotalPlusVoList.size()]; MgtMapTotalPlusVo mgtMapTotalPlusVo; for (int i = 0; i < mgtMapTotalPlusVoList.size(); i++) { mgtMapTotalPlusVo = mgtMapTotalPlusVoList.get(i); orderTotalKey[i] = mgtMapTotalPlusVo.getMapKey(); orderTotalValue[i] = mgtMapTotalPlusVo.getMapValueFirst(); orderMoneyValue[i] = mgtMapTotalPlusVo.getMapValueSecond(); } orderDistributionTotalVo.setOrderTotalKey(orderTotalKey); orderDistributionTotalVo.setOrderTotalValue(orderTotalValue); orderDistributionTotalVo.setOrderMoneyValue(orderMoneyValue); List<MgtMapTotalPlusVo> mgtMapTotalPlusVoList = orderMapper.listMerOrderDistributionTotal(merTotalDto); if (mgtMapTotalPlusVoList != null && !mgtMapTotalPlusVoList.isEmpty()) { String[] orderTotalKey = new String[mgtMapTotalPlusVoList.size()]; Integer[] orderTotalValue = new Integer[mgtMapTotalPlusVoList.size()]; BigDecimal[] orderMoneyValue = new BigDecimal[mgtMapTotalPlusVoList.size()]; MgtMapTotalPlusVo mgtMapTotalPlusVo; for (int i = 0; i < mgtMapTotalPlusVoList.size(); i++) { mgtMapTotalPlusVo = mgtMapTotalPlusVoList.get(i); orderTotalKey[i] = mgtMapTotalPlusVo.getMapKey(); orderTotalValue[i] = mgtMapTotalPlusVo.getMapValueFirst(); orderMoneyValue[i] = mgtMapTotalPlusVo.getMapValueSecond(); } orderDistributionTotalVo.setOrderTotalKey(orderTotalKey); orderDistributionTotalVo.setOrderTotalValue(orderTotalValue); orderDistributionTotalVo.setOrderMoneyValue(orderMoneyValue); } // 返回订单分布总数对象 return orderDistributionTotalVo; } @@ -3295,20 +3471,20 @@ } /** * @description 商户端订单管理统计 * @author jqs * @date 2023/7/31 10:53 * @param merOrderPageDto * @return MerTotalOrderVo * @return MerTotalOrderVo * @description 商户端订单管理统计 * @author jqs * @date 2023/7/31 10:53 */ @Override public MerTotalOrderVo totalMerOrder(MerOrderPageDto merOrderPageDto) { MerTotalOrderVo merTotalOrderVo = new MerTotalOrderVo(); if(StringUtils.isNotBlank(merOrderPageDto.getKeyword())){ if (StringUtils.isNotBlank(merOrderPageDto.getKeyword())) { MgtUserIdByKeywordDto userIdByKeywordDto = new MgtUserIdByKeywordDto(); userIdByKeywordDto.setKeyword(merOrderPageDto.getKeyword()); MgtUserIdByKeywordVo userIdByKeywordVo = remoteMemberService.getUserIdByKeyword(userIdByKeywordDto).getData(); if(StringUtils.isNotBlank(userIdByKeywordVo.getUserIds())){ if (StringUtils.isNotBlank(userIdByKeywordVo.getUserIds())) { List<Long> userIdList = Arrays.stream(userIdByKeywordVo.getUserIds().split(",")) .map(Long::parseLong) .collect(Collectors.toList()); @@ -3379,33 +3555,33 @@ } /** * @description 自动取消订单 * @author jqs * @date 2023/7/13 17:15 * @param orderId * @return void * @return void * @description 自动取消订单 * @author jqs * @date 2023/7/13 17:15 */ @Override @Transactional public void autoCancelOrder(String orderId){ public void autoCancelOrder(String orderId) { //更新订单信息 Order order = this.getById(orderId); order.setOrderStatus(0); order.setCancelTime(new Date()); this.saveOrUpdate(order); //活动订单回退库存 if(order.getOrderFrom()==2){ if (order.getOrderFrom() == 2) { List<OrderGoods> orderGoodsList = orderGoodsService.listByOrderId(orderId); if(orderGoodsList!=null&&!orderGoodsList.isEmpty()){ for(OrderGoods orderGoods : orderGoodsList){ if (orderGoodsList != null && !orderGoodsList.isEmpty()) { for (OrderGoods orderGoods : orderGoodsList) { //redis库存控制极简版 String goodsLock = "goods_lock_"+order.getActivityId()+"_"+orderGoods.getGoodsId(); String goodsLock = "goods_lock_" + order.getActivityId() + "_" + orderGoods.getGoodsId(); RLock redissonLock = redissonClient.getLock(goodsLock); try{ try { redissonLock.lock(30, TimeUnit.SECONDS); Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+order.getActivityId()+"-"+orderGoods.getGoodsId()); redisService.setCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+order.getActivityId()+"-"+orderGoods.getGoodsId(),surpNum+orderGoods.getBuyNum()); }finally{ Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + order.getActivityId() + "-" + orderGoods.getGoodsId()); redisService.setCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + order.getActivityId() + "-" + orderGoods.getGoodsId(), surpNum + orderGoods.getBuyNum()); } finally { redissonLock.unlock(); } //改变活动库存 @@ -3426,16 +3602,16 @@ remoteActivityService.delActivityRecord(activityRecord); } } }else{ if(order.getCouponMoney().compareTo(BigDecimal.ZERO)>0){ } else { if (order.getCouponMoney().compareTo(BigDecimal.ZERO) > 0) { //回退优惠券 List<OrderGoods> orderGoodsList = orderGoodsService.listByOrderId(orderId); if(orderGoodsList!=null&&!orderGoodsList.isEmpty()) { if (orderGoodsList != null && !orderGoodsList.isEmpty()) { StringJoiner memberCouponSJ = new StringJoiner(","); for (OrderGoods orderGoods : orderGoodsList) { memberCouponSJ.add(orderGoods.getCouponId()); } if(memberCouponSJ!=null){ if (memberCouponSJ != null) { remoteCouponService.backMemberCoupon(memberCouponSJ.toString()); } @@ -3445,17 +3621,17 @@ } /** * @description 订单支付回调 * @author jqs * @date 2023/7/13 17:57 * @param transaction * @return void * @return void * @description 订单支付回调 * @author jqs * @date 2023/7/13 17:57 */ @Override @Transactional public void payBack(Transaction transaction) { public void payBack(PartnerTransactionsResult transaction) { // 更新订单状态 String orderId=transaction.getOutTradeNo(); String orderId = transaction.getOutTradeNo(); Order order = this.getById(orderId); order.setOrderStatus(2); order.setPayTime(new Date()); @@ -3501,8 +3677,8 @@ goodsTotalChangeDto.setChangeType(1); goodsTotalChangeDto.setChangeNum(orderGoods.getBuyNum()); goodsTotalChangeDto.setMoney(orderGoods.getGoodsReceivableMoney()); Integer bugGoodsNum = orderMapper.countUserBuyGoodsNum(order.getUserId(),orderGoods.getGoodsId()); if(bugGoodsNum==null||bugGoodsNum<1){ Integer bugGoodsNum = orderMapper.countUserBuyGoodsNum(order.getUserId(), orderGoods.getGoodsId()); if (bugGoodsNum == null || bugGoodsNum < 1) { goodsTotalChangeDto.setPersonNum(1); } goodsTotalChangeDtoList.add(goodsTotalChangeDto); @@ -3586,16 +3762,16 @@ } /** * @description 订单退款 * @author jqs * @date 2023/7/13 18:36 * @param orderId * @return void * @return void * @description 订单退款 * @author jqs * @date 2023/7/13 18:36 */ @Override @Transactional public void refundOrder(String orderId){ //更新订单信息 public void refundOrder(String orderId) { // 更新订单信息 Order order = this.getById(orderId); order.setOrderStatus(0); order.setCancelTime(new Date()); @@ -3608,7 +3784,7 @@ orderRefund.setOrderId(order.getOrderId()); orderRefund.setUserId(order.getUserId()); orderRefund.setShopId(order.getShopId()); orderRefund.setRefundMoney(order.getPayMoney()); orderRefund.setRefundMoney(order.getOnlinePayMoney()); orderRefund.setOrderMoney(order.getPayMoney()); orderRefund.setCreateTime(new Date()); // 初始化各类商品的收款金额 @@ -3622,19 +3798,19 @@ Boolean haveExperience = false; Boolean haveGoods = false; List<OrderGoods> orderGoodsList = orderGoodsService.listByOrderId(orderId); if(orderGoodsList!=null&&!orderGoodsList.isEmpty()){ if (orderGoodsList != null && !orderGoodsList.isEmpty()) { StringJoiner memberCouponSJ = new StringJoiner(","); for(OrderGoods orderGoods : orderGoodsList){ if(order.getOrderFrom()==2){ for (OrderGoods orderGoods : orderGoodsList) { if (order.getOrderFrom() == 2) { //redis库存控制极简版 //活动订单回退库存 String goodsLock = "goods_lock_"+order.getActivityId()+"_"+orderGoods.getGoodsId(); String goodsLock = "goods_lock_" + order.getActivityId() + "_" + orderGoods.getGoodsId(); RLock redissonLock = redissonClient.getLock(goodsLock); try{ try { redissonLock.lock(30, TimeUnit.SECONDS); Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+order.getActivityId()+"-"+orderGoods.getGoodsId()); redisService.setCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+order.getActivityId()+"-"+orderGoods.getGoodsId(),surpNum+orderGoods.getBuyNum()); }finally{ Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + order.getActivityId() + "-" + orderGoods.getGoodsId()); redisService.setCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + order.getActivityId() + "-" + orderGoods.getGoodsId(), surpNum + orderGoods.getBuyNum()); } finally { redissonLock.unlock(); } //改变活动库存 @@ -3653,7 +3829,7 @@ activityRecord.setUserId(order.getUserId()); activityRecord.setOrderTime(order.getCreateTime()); remoteActivityService.delActivityRecord(activityRecord); }else{ } else { memberCouponSJ.add(orderGoods.getCouponId()); } switch (orderGoods.getGoodsType()) { @@ -3678,7 +3854,7 @@ } } //回退优惠券 if(memberCouponSJ!=null){ if (memberCouponSJ != null) { remoteCouponService.backMemberCoupon(memberCouponSJ.toString()); } } @@ -3737,43 +3913,97 @@ activityTotalChangeDto.setUserId(order.getUserId()); remoteActivityService.changeActivityTotal(activityTotalChangeDto); } // 用户取消订单退款 BigDecimal onlinePayMoney = order.getOnlinePayMoney(); if(BigDecimal.ZERO.compareTo(onlinePayMoney) < 0){ // 订单支付金额大于0,可发起退款 orderWxApplyRefund(orderId, refundId, onlinePayMoney, orderRefund); } orderRefund.setRefundStatus(1); orderRefundService.saveOrUpdate(orderRefund); } /** * @description 员工端活动订单统计 * @author jqs * @date 2023/7/17 15:25 * 申请退款API */ private void orderWxApplyRefund(String orderId, String outRefundNo, BigDecimal payMoney, OrderRefund orderRefund){ try { // 创建支付订单 OrderPayment orderPayment = orderPaymentService.getByOrderId(orderId); if (null == orderPayment) { return; } String subMchId = orderPayment.getSubMchId(); WxPayConfig config = wxService.getConfig(); RefundsRequest request = new RefundsRequest(); request.setSpAppid(config.getAppId()); request.setSubMchid(subMchId); request.setTransactionId(orderPayment.getTransactionId()); request.setOutRefundNo(outRefundNo); request.setReason("用户取消订单"); // 订单金额 int total = payMoney.multiply(new BigDecimal(100)).intValue(); RefundsRequest.Amount amount = RefundsRequest.Amount.builder().refund(total).total(total).currency("CNY").build(); request.setAmount(amount); request.setNotifyUrl(config.getNotifyUrl()); RefundsResult result = wxService.getEcommerceService().refunds(request); // 微信退款id orderRefund.setWxRefundId(result.getRefundId()); // 请求参数 Gson gson = new Gson(); String refundRequestJson = gson.toJson(request); // 返回参数 String refundResponseJson = gson.toJson(result); // 保存支付订单统一下单日志 paymentMessageService.savePaymentMessage("3", orderId, refundRequestJson, refundResponseJson); } catch (WxPayException e) { throw new ServiceException(e.getMessage()); } } /** * @param staffTotalDto * @return StaffActivityOrderTotalVo * @return StaffActivityOrderTotalVo * @description 员工端活动订单统计 * @author jqs * @date 2023/7/17 15:25 */ @Override public StaffActivityOrderTotalVo getStaffActivityOrderTotal(StaffTotalDto staffTotalDto){ public StaffActivityOrderTotalVo getStaffActivityOrderTotal(StaffTotalDto staffTotalDto) { StaffActivityOrderTotalVo staffActivityOrderTotalVo = orderMapper.getStaffActivityOrderTotal(staffTotalDto); return staffActivityOrderTotalVo; } /** * @description 员工端活动统计 * @author jqs * @date 2023/7/17 15:51 * @param staffTotalDto * @return StaffActivityTotalVo * @return StaffActivityTotalVo * @description 员工端活动统计 * @author jqs * @date 2023/7/17 15:51 */ @Override public StaffActivityTotalVo getStaffActivityTotal(StaffTotalDto staffTotalDto){ public StaffActivityTotalVo getStaffActivityTotal(StaffTotalDto staffTotalDto) { StaffActivityTotalVo staffActivityTotalVo = orderMapper.getStaffActivityTotal(staffTotalDto); return staffActivityTotalVo; } /** * @description 员工端活动统计订单分布 * @author jqs * @date 2023/7/17 16:16 * @param staffTotalDto * @return MerOrderDistributionTotalVo * @return MerOrderDistributionTotalVo * @description 员工端活动统计订单分布 * @author jqs * @date 2023/7/17 16:16 */ @Override public MerOrderDistributionTotalVo getStaffOrderDistributionTotal(StaffTotalDto staffTotalDto){ public MerOrderDistributionTotalVo getStaffOrderDistributionTotal(StaffTotalDto staffTotalDto) { MerOrderDistributionTotalVo orderDistributionTotalVo = new MerOrderDistributionTotalVo(); // 获取订单年龄用户列表 MerTotalDto merTotalDto = new MerTotalDto(); @@ -3814,7 +4044,7 @@ } // 如果用户ID列表不为空 if (userIdList != null && !userIdList.isEmpty()) { mgtMapTotalPlusVo = orderMapper.listStaffActivityOrderDistributionTotal(userIdList,staffTotalDto.getShopId(),staffTotalDto.getActivityId()); mgtMapTotalPlusVo = orderMapper.listStaffActivityOrderDistributionTotal(userIdList, staffTotalDto.getShopId(), staffTotalDto.getActivityId()); orderTotalValue[i] = mgtMapTotalPlusVo.getMapValueFirst(); orderMoneyValue[i] = mgtMapTotalPlusVo.getMapValueSecond(); } else { @@ -3833,14 +4063,14 @@ /** * @description 员工端活动统计销售占比 * @author jqs * @date 2023/7/17 17:03 * @param staffTotalDto * @return MerOrderTypeTotalVo * @return MerOrderTypeTotalVo * @description 员工端活动统计销售占比 * @author jqs * @date 2023/7/17 17:03 */ @Override public MerOrderTypeTotalVo getStaffOrderTypeTotal(StaffTotalDto staffTotalDto){ public MerOrderTypeTotalVo getStaffOrderTypeTotal(StaffTotalDto staffTotalDto) { MerOrderTypeTotalVo orderTypeTotalVo = new MerOrderTypeTotalVo(); //获取商品分类销售数据 List<MgtMapTotalPlusVo> MgtMapIntTotalVoGoodsType = orderMapper.listStaffOrderTotalGoodsType(staffTotalDto); @@ -3878,11 +4108,11 @@ } /** * @description 员工端获客人数 * @author jqs * @date 2023/7/17 18:42 * @param staffTotalDto * @return StaffActivityDateMemberTotalVo * @return StaffActivityDateMemberTotalVo * @description 员工端获客人数 * @author jqs * @date 2023/7/17 18:42 */ @Override public StaffActivityDateMemberTotalVo getStaffActivityGetMemberTotal(StaffTotalDto staffTotalDto) { @@ -3919,11 +4149,11 @@ } /** * @description 员工端获客人数 * @author jqs * @date 2023/7/17 18:42 * @param staffTotalDto * @return StaffActivityDateMemberTotalVo * @return StaffActivityDateMemberTotalVo * @description 员工端获客人数 * @author jqs * @date 2023/7/17 18:42 */ @Override public StaffActivityDateMemberTotalVo getStaffActivityMemberTotal(StaffTotalDto staffTotalDto) { @@ -3960,12 +4190,12 @@ } /** * @description 获取时间段日期 * @author jqs * @date 2023/7/17 19:11 * @param startDate * @param endDate * @return List<String> * @return List<String> * @description 获取时间段日期 * @author jqs * @date 2023/7/17 19:11 */ public static List<String> getDateRange(String startDate, String endDate) { List<String> dateList = new ArrayList<>(); @@ -3979,50 +4209,87 @@ } /** * @description 检查订单状态定时任务 * @author jqs * @date 2023/7/25 14:40 * @param * @return void * @return void * @description 检查订单状态定时任务 * @author jqs * @date 2023/7/25 14:40 */ @Override public void checkOrderStatus(){ public void checkOrderStatus() { Integer delayTime = 30; if(redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)){ if (redisService.hasKey(SecurityConstant.AUTO_CANCEL_ORDER_TIME)) { delayTime = redisService.getCacheObject(SecurityConstant.AUTO_CANCEL_ORDER_TIME); } delayTime = delayTime + 5; Date checkTime = DateUtils.addMinutes(new Date(),-delayTime); Date checkTime = DateUtils.addMinutes(new Date(), -delayTime); LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Order::getDelFlag,0); queryWrapper.lt(Order::getCreateTime,checkTime); queryWrapper.eq(Order::getOrderStatus,1); queryWrapper.eq(Order::getDelFlag, 0); queryWrapper.lt(Order::getCreateTime, checkTime); queryWrapper.eq(Order::getOrderStatus, 1); List<Order> orderList = this.list(queryWrapper); if(orderList!=null&&!orderList.isEmpty()){ for(Order order: orderList){ if (orderList != null && !orderList.isEmpty()) { for (Order order : orderList) { autoTimeCancelOrder(order); } } } private void autoTimeCancelOrder(Order order){ @Override public void profitSharingBack(ProfitSharingNotifyResult result) { try { String outOrderNo = result.getOutOrderNo(); ProfitSharing profitSharing = profitSharingService.getById(outOrderNo); if(null != profitSharing){ profitSharing.setWxOrderId(result.getOrderId()); profitSharing.setFinishTime(result.getSuccessTime()); profitSharing.setFinishFlag(1); profitSharingService.saveOrUpdate(profitSharing); } } catch (Exception e){ log.error("==分账回成功回调操作====【{}】========={}", result.getOutOrderNo(), e.getMessage()); } } @Override public void orderRefundBack(RefundNotifyResult result) { try { String outRefundNo = result.getOutRefundNo(); String refundStatus = result.getRefundStatus(); OrderRefund orderRefund = orderRefundService.getById(outRefundNo); if(null != orderRefund){ orderRefund.setWxRefundId(result.getRefundId()); orderRefund.setBackTime(result.getSuccessTime()); orderRefund.setRefundStatus(IDict.getCodeByText(RefundStatusEnum.class, refundStatus)); orderRefundService.saveOrUpdate(orderRefund); } } catch (Exception e){ log.error("==退款回成功回调操作====【{}】========={}", result.getOutRefundNo(), e.getMessage()); } } private void autoTimeCancelOrder(Order order) { String orderId = order.getOrderId(); order.setOrderStatus(0); order.setCancelTime(new Date()); this.saveOrUpdate(order); //活动订单回退库存 if(order.getOrderFrom()==2){ if (order.getOrderFrom() == 2) { List<OrderGoods> orderGoodsList = orderGoodsService.listByOrderId(orderId); if(orderGoodsList!=null&&!orderGoodsList.isEmpty()){ for(OrderGoods orderGoods : orderGoodsList){ if (orderGoodsList != null && !orderGoodsList.isEmpty()) { for (OrderGoods orderGoods : orderGoodsList) { //redis库存控制极简版 String goodsLock = "goods_lock_"+order.getActivityId()+"_"+orderGoods.getGoodsId(); String goodsLock = "goods_lock_" + order.getActivityId() + "_" + orderGoods.getGoodsId(); RLock redissonLock = redissonClient.getLock(goodsLock); try{ try { redissonLock.lock(30, TimeUnit.SECONDS); Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+order.getActivityId()+"-"+orderGoods.getGoodsId()); redisService.setCacheObject(SecurityConstant.ACTIVITY_GOODS+"-"+order.getActivityId()+"-"+orderGoods.getGoodsId(),surpNum+orderGoods.getBuyNum()); }finally{ Integer surpNum = redisService.getCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + order.getActivityId() + "-" + orderGoods.getGoodsId()); redisService.setCacheObject(SecurityConstant.ACTIVITY_GOODS + "-" + order.getActivityId() + "-" + orderGoods.getGoodsId(), surpNum + orderGoods.getBuyNum()); } finally { redissonLock.unlock(); } //改变活动库存 @@ -4043,16 +4310,16 @@ remoteActivityService.delActivityRecord(activityRecord); } } }else{ if(order.getCouponMoney().compareTo(BigDecimal.ZERO)>0){ } else { if (order.getCouponMoney().compareTo(BigDecimal.ZERO) > 0) { //回退优惠券 List<OrderGoods> orderGoodsList = orderGoodsService.listByOrderId(orderId); if(orderGoodsList!=null&&!orderGoodsList.isEmpty()) { if (orderGoodsList != null && !orderGoodsList.isEmpty()) { StringJoiner memberCouponSJ = new StringJoiner(","); for (OrderGoods orderGoods : orderGoodsList) { memberCouponSJ.add(orderGoods.getCouponId()); } if(memberCouponSJ!=null){ if (memberCouponSJ != null) { remoteCouponService.backMemberCoupon(memberCouponSJ.toString()); } ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/order/OrderService.java
@@ -2,6 +2,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.github.binarywang.wxpay.bean.ecommerce.PartnerTransactionsResult; import com.github.binarywang.wxpay.bean.ecommerce.RefundNotifyResult; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult; import com.ruoyi.order.domain.dto.*; import com.ruoyi.order.domain.pojo.order.Order; import com.ruoyi.order.domain.vo.*; @@ -416,7 +419,7 @@ * @param Transaction transaction * @return void */ void payBack(Transaction transaction); void payBack(PartnerTransactionsResult transaction); /** * 取消订单 @@ -491,6 +494,18 @@ void checkOrderStatus(); /** * 分账通知回调 * @param result */ void profitSharingBack(ProfitSharingNotifyResult result); /** * 订单退款回调 * @param result */ void orderRefundBack(RefundNotifyResult result); /** * @description 获取商户订单统计 * @author jqs * @date 2023/8/4 10:25 ruoyi-modules/ruoyi-order/src/main/resources/bootstrap.yml
@@ -3,7 +3,7 @@ port: 10064 # Spring spring: spring: application: # 应用名称 name: ruoyi-order @@ -25,3 +25,13 @@ # 共享配置 shared-configs: - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} wx: pay: appId: #微信公众号或者小程序等的appid(平台) mchId: #微信支付商户号(平台) mchKey: #微信支付商户密钥(平台) platformTyMacId: # 平台特约商户号,用户平台收取服务费 subAppId: #服务商模式下的子商户公众账号ID (这个可以不用配置) subMchId: #服务商模式下的子商户号 (二级商户号 也不用配置) keyPath: # (商户平台 下载的V3证书)p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头) ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/controller/console/ShopController.java
@@ -2,6 +2,9 @@ import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.bean.BeanUtils; import com.ruoyi.shop.domain.pojo.shop.ShopAuthentication; import com.ruoyi.shop.domain.pojo.shop.ShopProportion; import com.ruoyi.shop.domain.pojo.shop.ShopRelUser; import com.ruoyi.shop.domain.pojo.shop.ShopStaff; import com.ruoyi.shop.service.shop.*; @@ -10,6 +13,7 @@ import com.ruoyi.system.api.constant.AppErrorConstant; import com.ruoyi.system.api.domain.dto.*; import com.ruoyi.system.api.domain.poji.shop.Shop; import com.ruoyi.system.api.domain.poji.shop.ShopProportionVo; import com.ruoyi.system.api.domain.poji.sys.SysUser; import com.ruoyi.system.api.domain.vo.*; import com.ruoyi.system.api.model.QwH5LoginVo; @@ -59,6 +63,12 @@ @Resource private MemberTaskService memberTaskService; @Resource private ShopAuthenticationService shopAuthenticationService; @Resource private ShopProportionService shopProportionService; /** * 企业微信H5登录 * @param qwUserDetail @@ -90,6 +100,30 @@ return R.ok(shop); } @PostMapping("/getShopSubMchId") public R<String> getShopSubMchId(@RequestBody Long shopId){ String subMchid = ""; ShopAuthentication authentication = shopAuthenticationService.getByShopId(shopId); if(null != authentication){ subMchid = authentication.getSubMchid(); } return R.ok(subMchid, ""); } /** * 获取商户分成 * @param shopId * @return */ @PostMapping("/getShopProportion") public R<ShopProportionVo> getShopProportion(@RequestBody Long shopId){ ShopProportion shopProportion = shopProportionService.getByShopId(shopId); ShopProportionVo shopProportionVo = new ShopProportionVo(); BeanUtils.copyBeanProp(shopProportionVo, shopProportion); return R.ok(shopProportionVo); } @PostMapping("/getShopByUserId") public R<ShopRelUserVo> getShopByUserId(@RequestBody Long userId) ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/enums/WxApplyMentSignStateEnum.java
New file @@ -0,0 +1,20 @@ package com.ruoyi.shop.enums; import com.ruoyi.shop.enums.dict.IDict; public enum WxApplyMentSignStateEnum implements IDict<Integer> { /** * 签约状态 1、UNSIGNED:未签约。该状态下,电商平台可查询获取签约链接,引导二级商户的超级管理员完成签约; 2、SIGNED :已签约。指二级商户的超级管理员已完成签约。注意:若申请单被驳回,商户修改了商户主体名称、法人名称、超级管理员信息、主体类型等信息,则需重新签约。 3、NOT_SIGNABLE:不可签约。该状态下,暂不支持超级管理员签约。一般为申请单处于已驳回、已冻结、机器校验中状态,无法签约。 */ UNSIGNED(1, "UNSIGNED"), SIGNED(2, "SIGNED"), NOT_SIGNABLE(3, "NOT_SIGNABLE"); WxApplyMentSignStateEnum(Integer code, String text){ init(code, text); } } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/enums/WxApplyMentStateEnum.java
New file @@ -0,0 +1,30 @@ package com.ruoyi.shop.enums; import com.ruoyi.shop.enums.dict.IDict; public enum WxApplyMentStateEnum implements IDict<Integer> { /** * 审核状态 1:CHECKING:资料校验中 2:ACCOUNT_NEED_VERIFY:待账户验证 3:AUDITING:审核中 4:REJECTED:已驳回 5:NEED_SIGN:待签约 6:FINISH:完成 7:FROZEN:已冻结 8:CANCELED:已作废 */ CHECKING(1, "CHECKING"), ACCOUNT_NEED_VERIFY(2, "ACCOUNT_NEED_VERIFY"), AUDITING(3, "AUDITING"), REJECTED(4, "REJECTED"), NEED_SIGN(5, "NEED_SIGN"), FINISH(6, "FINISH"), FROZEN(7, "FROZEN"), CANCELED(8, "CANCELED"); WxApplyMentStateEnum(Integer code, String text){ init(code, text); } } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/enums/dict/DictBean.java
New file @@ -0,0 +1,15 @@ package com.ruoyi.shop.enums.dict; import lombok.Data; /** * 字典bean * 只有code和text,可用于展示下拉框 * * @author luozhan */ @Data public class DictBean<T> implements IDict<T> { private final T code; private final String text; } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/enums/dict/IDict.java
New file @@ -0,0 +1,170 @@ package com.ruoyi.shop.enums.dict; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 字典接口 * <p> * 自定义的字典枚举类实现本接口后可省略属性code和text,以及对应的get方法 * 在构造方法中只需调用init方法即可初始化 * * @author luozhan * @date 2021-12 */ public interface IDict<T> { /** * 通过code获取value * * @param clazz 枚举class * @param code code * @return text */ static <T> String getTextByCode(Class<? extends IDict<T>> clazz, T code) { return Stream.of(clazz.getEnumConstants()) .filter((IDict<T> e) -> e.getCode().equals(code)) .map(IDict::getText) .findAny().orElse(""); } /** * 通过text获取code * * @param clazz 枚举class * @param text text * @return code */ static <T> T getCodeByText(Class<? extends IDict<T>> clazz, String text) { return Stream.of(clazz.getEnumConstants()) .filter((IDict<T> e) -> e.getText().equals(text)) .map(IDict::getCode) .findAny().orElse(null); } /** * 通过code获取字典枚举实例 * * @param clazz 枚举class * @param code code * @param <T> 字典code类型 * @param <R> 枚举类型 * @return 字典枚举实例 */ @SuppressWarnings("unchecked") static <T, R extends IDict<T>> R getByCode(Class<? extends IDict<T>> clazz, T code) { return Stream.of(clazz.getEnumConstants()) .filter((IDict<T> e) -> (e.getCode().equals(code))) .map(v -> (R) v) .findAny() .orElse(null); } /** * 获取给定的字典枚举项(常用下拉框数据请求) * * @param enums 可指定需要哪些项 * @return List */ @SafeVarargs static <T, E extends IDict<T>> List<IDict<T>> getItems(E... enums) { return Stream.of(enums) .map(DictPool::getDict) .collect(Collectors.toList()); } /** * 获取所有字典枚举项,除开指定的枚举 * * @param exclude 指定排除的枚举 * @return List */ @SafeVarargs @SuppressWarnings("unchecked") static <T, E extends IDict<T>> List<IDict<T>> getItemsExclude(E... exclude) { Class<IDict<T>> clazz = (Class<IDict<T>>) exclude.getClass().getComponentType(); IDict<T>[] allEnum = clazz.getEnumConstants(); List<IDict<T>> excludeList = Arrays.asList(exclude); return Stream.of(allEnum) .filter(e -> !excludeList.contains(e)) .map(DictPool::getDict) .collect(Collectors.toList()); } /** * 获取所有字典枚举项(常用下拉框数据请求) * 枚举值上标记@Deprecated的不会返回 * * @param clazz 字典枚举类 * @return List */ static <T> List<IDict<T>> getAll(Class<? extends IDict<T>> clazz) { Map<String, Field> fieldCache = Arrays.stream(clazz.getDeclaredFields()). filter(Field::isEnumConstant). collect(Collectors.toMap(Field::getName, Function.identity())); IDict<T>[] allEnum = clazz.getEnumConstants(); return Stream.of(allEnum) .filter(e -> !fieldCache.get(((Enum<?>) e).name()).isAnnotationPresent(Deprecated.class)) .map(DictPool::getDict) .collect(Collectors.toList()); } /** * 初始化 * * @param code 字典编码 * @param text 字典文本 */ default void init(T code, String text) { DictPool.putDict(this, code, text); } /** * 获取编码 * * @return 编码 */ default T getCode() { return DictPool.getDict(this).getCode(); } /** * 获取文本 * * @return 文本 */ default String getText() { return DictPool.getDict(this).getText(); } @SuppressWarnings("all") class DictPool { private static final Map<IDict, DictBean> DICT_MAP = new ConcurrentHashMap<>(); private static final Map<String, Class<? extends IDict>> DICT_NAME_CLASS_MAP = new ConcurrentHashMap<>(); static <T> void putDict(IDict<T> dict, T code, String text) { DICT_NAME_CLASS_MAP.put(dict.getClass().getName(), dict.getClass()); DICT_MAP.put(dict, new DictBean<>(code, text)); } public static List<IDict<Object>> getDict(String dictName) { Class<? extends IDict> aClass = DICT_NAME_CLASS_MAP.get(dictName); return IDict.getAll((Class<? extends IDict<Object>>) aClass); } static <K extends IDict<T>, T> DictBean<T> getDict(K dict) { return DICT_MAP.get(dict); } } } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/scheduler/ShopScheduler.java
@@ -1,7 +1,12 @@ package com.ruoyi.shop.scheduler; import com.ruoyi.common.core.utils.bean.BeanUtils; import com.ruoyi.common.core.utils.uuid.IdUtils; import com.ruoyi.shop.domain.pojo.shop.ShopAuthentication; import com.ruoyi.shop.service.shop.ShopService; import com.ruoyi.shop.service.task.ShopTaskService; import com.ruoyi.system.api.domain.poji.shop.Shop; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; @@ -21,6 +26,9 @@ @Resource private ShopTaskService shopTaskService; @Resource private ShopService shopService; /** * 定时检查跟进任务状态 @@ -33,4 +41,16 @@ } } /** * 0 0/1 * * * ? * 定时检查 每分钟检查一次 微信二级商户进件状态 */ @Scheduled(cron="0 0/1 * * * ?") private void queryEcommerceApplyMentsStatus(){ if(schedulerUtils.getSchedulerRun()) { log.info("--------------------定时检查微信二级商户进件状态任务开始执行--------------------"); shopService.queryEcommerceApplyMentsStatus(); log.info("--------------------定时检查微信二级商户进件状态任务结束执行--------------------"); } } } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopAuthenticationServiceImpl.java
@@ -1,10 +1,23 @@ package com.ruoyi.shop.service.impl.shop; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; import com.google.common.base.Joiner; import com.ruoyi.shop.domain.pojo.shop.ShopAuthentication; import com.ruoyi.shop.enums.WxApplyMentSignStateEnum; import com.ruoyi.shop.enums.WxApplyMentStateEnum; import com.ruoyi.shop.enums.dict.IDict; import com.ruoyi.shop.mapper.shop.ShopAuthenticationMapper; import com.ruoyi.shop.service.shop.ShopAuthenticationService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * <p> @@ -17,4 +30,72 @@ @Service public class ShopAuthenticationServiceImpl extends ServiceImpl<ShopAuthenticationMapper, ShopAuthentication> implements ShopAuthenticationService { @Override public List<ShopAuthentication> getShopAuthNeedUpdateStatus() { List<Integer> auditStatusList = Arrays.asList(1, 2, 3, 5); LambdaQueryWrapper<ShopAuthentication> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.in(ShopAuthentication::getAuditStatus, auditStatusList); queryWrapper.isNotNull(ShopAuthentication::getApplymentId); return this.list(queryWrapper); } @Override public void updateAuditStatusByApplymentId(String applymentId, ApplymentsStatusResult result) { // WxApplyMentStateEnum String applymentState = result.getApplymentState(); Integer auditStatus = IDict.getCodeByText(WxApplyMentStateEnum.class, applymentState); Integer signState = IDict.getCodeByText(WxApplyMentSignStateEnum.class, result.getSignState()); LambdaUpdateWrapper<ShopAuthentication> updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.eq(ShopAuthentication::getApplymentId, applymentId) .set(ShopAuthentication::getAuditStatus, auditStatus) .set(ShopAuthentication::getSignState, signState); if(WxApplyMentStateEnum.ACCOUNT_NEED_VERIFY.getCode().equals(auditStatus)){ // ACCOUNT_NEED_VERIFY 待账户验证 updateWrapper.set(ShopAuthentication::getLegalValidationUrl, result.getLegalValidationUrl()); ApplymentsStatusResult.AccountValidation accountValidation = result.getAccountValidation(); if(null != accountValidation) { updateWrapper.set(ShopAuthentication::getAvAccountName, accountValidation.getAccountName()) .set(ShopAuthentication::getAvAccountNo, accountValidation.getAccountNo()) .set(ShopAuthentication::getAvPayAmount, accountValidation.getPayAmount()) .set(ShopAuthentication::getDaNumber, accountValidation.getDestinationAccountNumber()) .set(ShopAuthentication::getDaName, accountValidation.getDestinationAccountName()) .set(ShopAuthentication::getDaBank, accountValidation.getDestinationAccountBank()) .set(ShopAuthentication::getDaCity, accountValidation.getCity()) .set(ShopAuthentication::getDaRemark, accountValidation.getRemark()) .set(ShopAuthentication::getDaDeadline, accountValidation.getDeadline()); } } else if(WxApplyMentStateEnum.NEED_SIGN.getCode().equals(auditStatus)){ // NEED_SIGN 待签约 updateWrapper.set(ShopAuthentication::getSignUrl, result.getSignUrl()); updateWrapper.set(ShopAuthentication::getSubMchid, result.getSubMchid()); } else if(WxApplyMentStateEnum.FINISH.getCode().equals(auditStatus)){ // FINISH 完成 updateWrapper.set(ShopAuthentication::getSubMchid, result.getSubMchid()); } else if(WxApplyMentStateEnum.REJECTED.getCode().equals(auditStatus) || WxApplyMentStateEnum.FROZEN.getCode().equals(auditStatus)){ List<ApplymentsStatusResult.AuditDetail> auditDetail = result.getAuditDetail(); if(null != auditDetail && !auditDetail.isEmpty()){ List<String> paramNameList = auditDetail.stream().map(ApplymentsStatusResult.AuditDetail::getParamName).collect(Collectors.toList()); List<String> rejectReason = auditDetail.stream().map(ApplymentsStatusResult.AuditDetail::getRejectReason).collect(Collectors.toList()); updateWrapper.set(ShopAuthentication::getAdParamName, Joiner.on(";").join(paramNameList)) .set(ShopAuthentication::getAdRejectReason, Joiner.on(";").join(rejectReason)); } } this.update(updateWrapper); } @Override public ShopAuthentication getByShopId(Long shopId) { LambdaQueryWrapper<ShopAuthentication> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(ShopAuthentication::getShopId, shopId) .last(" limit 1 "); return this.getOne(queryWrapper); } } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopProportionServiceImpl.java
@@ -1,7 +1,10 @@ package com.ruoyi.shop.service.impl.shop; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.shop.domain.dto.MgtShopProportionEditDto; import com.ruoyi.shop.domain.pojo.shop.ShopAuthentication; import com.ruoyi.shop.domain.pojo.shop.ShopProportion; import com.ruoyi.shop.mapper.shop.ShopProportionMapper; import com.ruoyi.shop.service.shop.ShopProportionService; @@ -48,4 +51,13 @@ shopProportionNew.setUpdateUserId(mgtShopProportionEditDto.getUserId()); this.saveOrUpdate(shopProportionNew); } @Override public ShopProportion getByShopId(Long shopId) { LambdaQueryWrapper<ShopProportion> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(ShopProportion::getShopId, shopId) .eq(ShopProportion::getDelFlag, 0) .last(" limit 1 "); return this.getOne(queryWrapper); } } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopServiceImpl.java
@@ -2,9 +2,12 @@ import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult; import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; import com.github.binarywang.wxpay.exception.WxPayException; import com.ruoyi.common.core.constant.CacheConstants; import com.ruoyi.common.core.exception.ServiceException; @@ -18,11 +21,15 @@ import com.ruoyi.shop.domain.pojo.shop.*; import com.ruoyi.shop.domain.pojo.task.ShopFile; import com.ruoyi.shop.domain.vo.*; import com.ruoyi.shop.enums.WxApplyMentStateEnum; import com.ruoyi.shop.mapper.shop.ShopMapper; import com.ruoyi.shop.service.shop.*; import com.ruoyi.shop.service.task.MemberTaskService; import com.ruoyi.shop.service.task.ShopFileService; import com.ruoyi.shop.service.task.ShopTaskService; import com.ruoyi.shop.util.WechatPayUtils; import com.ruoyi.shop.util.WxShopUtils; import com.ruoyi.shop.util.dto.*; import com.ruoyi.system.api.constant.AppErrorConstant; import com.ruoyi.system.api.domain.dto.*; import com.ruoyi.system.api.domain.poji.config.SysTag; @@ -115,11 +122,12 @@ @Resource private RedisService redisService; /*@Resource private WechatPayUtils wechatPayUtils;*/ @Resource private WechatPayUtils wechatPayUtils; /** * 获取商户详情 * * @param shopId * @return */ @@ -968,7 +976,7 @@ shopAuthentication.setApplyNumber(applyNumber); shopAuthentication.setAuditStatus(3); shopAuthenticationService.saveOrUpdate(shopAuthentication); //wechatPayUtils.ecommerceApply(shopAuthentication,applyNumber,shop); wechatPayUtils.ecommerceApply(shopAuthentication,applyNumber,shop); } /** @@ -1336,4 +1344,44 @@ shop.setUpdateTime(new Date()); shopMapper.updateShop(shop); } @Override public void queryEcommerceApplyMentsStatus() { List<ShopAuthentication> list = shopAuthenticationService.getShopAuthNeedUpdateStatus(); list.forEach(item -> queryApplyStatusByApplymentId(item.getApplymentId(), item.getShopId())); } /** * 调用微信接口查询申请审核状态 * @param applymentId */ private void queryApplyStatusByApplymentId(String applymentId, Long shopId) { try { if (StringUtils.isNotBlank(applymentId)) { ApplymentsStatusResult result = wechatPayUtils.queryApplyStatusByApplymentId(applymentId); shopAuthenticationService.updateAuditStatusByApplymentId(applymentId, result); String applymentState = result.getApplymentState(); if(WxApplyMentStateEnum.FINISH.getText().equals(applymentState)){ applySuccessUpdateShopStatus(shopId); } } } catch (WxPayException e) { e.printStackTrace(); } } /** * 商户进件审核完成,更新商户状态 * @param shopId */ private void applySuccessUpdateShopStatus(Long shopId){ // 更新商户状态为正常 Shop shop = this.getById(shopId); Integer shopStatus = handelShopStatus(shop.getFrozenFlag(), shop.getCooperativeFlag(), 1); LambdaUpdateWrapper<Shop> updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.eq(Shop::getShopId, shopId) .set(Shop::getShopStatus, shopStatus) .set(Shop::getAuthFlag, 1); this.update(updateWrapper); } } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopAuthenticationService.java
@@ -1,7 +1,10 @@ package com.ruoyi.shop.service.shop; import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; import com.ruoyi.shop.domain.pojo.shop.ShopAuthentication; import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; /** * <p> @@ -13,4 +16,23 @@ */ public interface ShopAuthenticationService extends IService<ShopAuthentication> { /** * 获取需要更新状态的 微信二级商户认证审核 * @return */ List<ShopAuthentication> getShopAuthNeedUpdateStatus(); /** * 修改微信审核状态 * @param applymentId * @param result */ void updateAuditStatusByApplymentId(String applymentId, ApplymentsStatusResult result); /** * 获取商户认证信息 * @param shopId * @return */ ShopAuthentication getByShopId(Long shopId); } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopProportionService.java
@@ -22,4 +22,11 @@ * @return void */ void editMgtShopProportion(MgtShopProportionEditDto mgtShopProportionEditDto); /** * 获取商户分成比例 * @param shopId * @return */ ShopProportion getByShopId(Long shopId); } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopService.java
@@ -327,4 +327,9 @@ * @return void */ void editShopCustomStatus(StaffShopCCEditDto staffShopCCEditDto); /** * 微信二级商户进件状态 定时任务检测 */ void queryEcommerceApplyMentsStatus(); } ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/WechatPayUtils.java
@@ -1,8 +1,18 @@ package com.ruoyi.shop.util; import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest; import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult; import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.EcommerceService; import com.github.binarywang.wxpay.service.WxPayService; import com.ruoyi.shop.domain.pojo.shop.ShopAuthentication; import com.ruoyi.system.api.domain.poji.shop.Shop; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * 微信支付 @@ -11,12 +21,12 @@ @AllArgsConstructor public class WechatPayUtils { //private final WxPayService wxService; private final WxPayService wxService; /** * 电商二级商户进件(提交申请单) */ /*public void ecommerceApply(ShopAuthentication shopAuthentication, String applyNumber, Shop shop) throws WxPayException { public ApplymentsResult ecommerceApply(ShopAuthentication shopAuthentication, String applyNumber, Shop shop) throws WxPayException { EcommerceService ecommerceService = wxService.getEcommerceService(); ApplymentsRequest request = new ApplymentsRequest(); //生成提交类 @@ -96,18 +106,18 @@ request.setBusinessAdditionPics(shopAuthentication.getBaPics()); request.setBusinessAdditionDesc(shopAuthentication.getBaDesc()); ApplymentsResult result = ecommerceService.createApply(request); return ecommerceService.createApply(request); } *//** /** * 通过查询申请状态API查询二级商户入驻申请结果 * @param applyNumber 业务申请编号 * @param applymentId 微信支付申请单号 * @return 申请状态 * @throws WxPayException *//* public ApplymentsStatusResult queryApplyStatusByOutRequestNo(String applyNumber) throws WxPayException { return wxService.getEcommerceService().queryApplyStatusByOutRequestNo(applyNumber); }*/ */ public ApplymentsStatusResult queryApplyStatusByApplymentId(String applymentId) throws WxPayException { return wxService.getEcommerceService().queryApplyStatusByApplymentId(applymentId); } }