Bladeren bron

Merge remote-tracking branch 'origin/master'

SheepHy 1 dag geleden
bovenliggende
commit
b15235ef1d
22 gewijzigde bestanden met toevoegingen van 588 en 106 verwijderingen
  1. 1 1
      src/main/java/com/zsElectric/boot/auth/controller/AuthController.java
  2. 5 1
      src/main/java/com/zsElectric/boot/auth/model/dto/WxMiniAppPhoneCodeLoginDTO.java
  3. 2 1
      src/main/java/com/zsElectric/boot/auth/service/impl/AuthServiceImpl.java
  4. 1 1
      src/main/java/com/zsElectric/boot/business/UserRefundsOrderInfo.java
  5. 8 0
      src/main/java/com/zsElectric/boot/business/controller/applet/AppletOrderController.java
  6. 4 0
      src/main/java/com/zsElectric/boot/business/model/entity/ChargeOrderInfo.java
  7. 3 0
      src/main/java/com/zsElectric/boot/business/model/form/ChargeOrderInfoForm.java
  8. 4 1
      src/main/java/com/zsElectric/boot/business/model/vo/ChargeOrderInfoVO.java
  9. 0 5
      src/main/java/com/zsElectric/boot/business/model/vo/applet/AppletUserInfoVO.java
  10. 13 0
      src/main/java/com/zsElectric/boot/business/service/UserAccountService.java
  11. 3 3
      src/main/java/com/zsElectric/boot/business/service/UserInfoService.java
  12. 2 0
      src/main/java/com/zsElectric/boot/business/service/UserOrderInfoService.java
  13. 51 0
      src/main/java/com/zsElectric/boot/business/service/impl/UserAccountServiceImpl.java
  14. 23 12
      src/main/java/com/zsElectric/boot/business/service/impl/UserInfoServiceImpl.java
  15. 331 66
      src/main/java/com/zsElectric/boot/business/service/impl/UserOrderInfoServiceImpl.java
  16. 5 0
      src/main/java/com/zsElectric/boot/common/constant/SystemConstants.java
  17. 75 0
      src/main/java/com/zsElectric/boot/core/pay/WechatCallbackRefundData.java
  18. 3 3
      src/main/java/com/zsElectric/boot/core/pay/WechatConstants.java
  19. 18 0
      src/main/java/com/zsElectric/boot/core/pay/WechatRefundCallback.java
  20. 13 1
      src/main/java/com/zsElectric/boot/security/model/WxMiniAppPhoneCodeAuthenticationToken.java
  21. 23 10
      src/main/java/com/zsElectric/boot/security/provider/WxMiniAppPhoneCodeAuthenticationProvider.java
  22. 0 1
      src/main/resources/mapper/business/UserInfoMapper.xml

+ 1 - 1
src/main/java/com/zsElectric/boot/auth/controller/AuthController.java

@@ -100,7 +100,7 @@ public class AuthController {
     @Operation(summary = "微信小程序登录(手机号Code-新版)")
     @PostMapping("/wx/miniapp/phone-code-login")
     public Result<AuthenticationToken> loginByWxMiniAppPhoneCode(@RequestBody @Valid WxMiniAppPhoneCodeLoginDTO loginDTO) {
-        log.info("收到手机号Code登录请求, code: {}", loginDTO.getCode());
+        log.info("收到手机号Code登录请求, code: {}, phoneCode: {}", loginDTO.getCode(), loginDTO.getPhoneCode());
         AuthenticationToken token = authService.loginByWxMiniAppPhoneCode(loginDTO);
         log.info("手机号Code登录成功");
         return Result.success(token);

+ 5 - 1
src/main/java/com/zsElectric/boot/auth/model/dto/WxMiniAppPhoneCodeLoginDTO.java

@@ -15,8 +15,12 @@ import jakarta.validation.constraints.NotBlank;
 @Data
 public class WxMiniAppPhoneCodeLoginDTO {
 
-    @Schema(description = "微信小程序getPhoneNumber获取的code", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Schema(description = "微信小程序wx.login获取的code,用于换取openid", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotBlank(message = "code不能为空")
     private String code;
 
+    @Schema(description = "微信小程序getPhoneNumber获取的code,用于获取手机号", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "phoneCode不能为空")
+    private String phoneCode;
+
 }

+ 2 - 1
src/main/java/com/zsElectric/boot/auth/service/impl/AuthServiceImpl.java

@@ -279,7 +279,8 @@ public class AuthServiceImpl implements AuthService {
     public AuthenticationToken loginByWxMiniAppPhoneCode(WxMiniAppPhoneCodeLoginDTO loginDTO) {
         // 创建微信小程序手机号Code认证Token
         WxMiniAppPhoneCodeAuthenticationToken authenticationToken = new WxMiniAppPhoneCodeAuthenticationToken(
-                loginDTO.getCode()
+                loginDTO.getCode(),
+                loginDTO.getPhoneCode()
         );
 
         // 执行认证

+ 1 - 1
src/main/java/com/zsElectric/boot/business/UserRefundsOrderInfo.java

@@ -24,7 +24,7 @@ public class UserRefundsOrderInfo extends BaseEntity {
     /**
      * 订单ID
      */
-    private String orderId;
+    private Long orderId;
     /**
      * 商户订单号
      */

+ 8 - 0
src/main/java/com/zsElectric/boot/business/controller/applet/AppletOrderController.java

@@ -8,6 +8,8 @@ import com.zsElectric.boot.business.service.RechargeLevelService;
 import com.zsElectric.boot.business.service.UserOrderInfoService;
 import com.zsElectric.boot.common.annotation.RepeatSubmit;
 import com.zsElectric.boot.common.constant.SystemConstants;
+import com.zsElectric.boot.core.pay.WechatCallbackRefundData;
+import com.zsElectric.boot.core.pay.WechatRefundCallback;
 import com.zsElectric.boot.core.web.Result;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -117,4 +119,10 @@ public class AppletOrderController {
     public Result<String> refundOrder() throws Exception {
         return Result.success(userOrderInfoService.refundOrder());
     }
+
+    @Operation(summary = "订单退款回调")
+    @PostMapping("/callback/refundOrderNotify")
+    public Map<String, Object> refundOrderNotify(HttpServletRequest request, HttpServletResponse response) {
+        return userOrderInfoService.refundCallback(request, response);
+    }
 }

+ 4 - 0
src/main/java/com/zsElectric/boot/business/model/entity/ChargeOrderInfo.java

@@ -124,6 +124,10 @@ public class ChargeOrderInfo extends BaseEntity {
      * 需要补缴的总金额
      */
     private BigDecimal totalMaspMoney;
+    /**
+     * 已补缴的金额
+     */
+    private BigDecimal alreadyMaspMoney;
     /**
      * 补缴状态  0.无需补缴  1.待补缴  2.已补缴
      */

+ 3 - 0
src/main/java/com/zsElectric/boot/business/model/form/ChargeOrderInfoForm.java

@@ -115,6 +115,9 @@ public class ChargeOrderInfoForm implements Serializable {
     @Schema(description = "需要补缴的总金额")
     private BigDecimal totalMaspMoney;
 
+    @Schema(description = "已补缴的金额")
+    private BigDecimal alreadyMaspMoney;
+
     @Schema(description = "补缴状态  0.无需补缴  1.待补缴  2.已补缴")
     private Integer maspStatus;
 

+ 4 - 1
src/main/java/com/zsElectric/boot/business/model/vo/ChargeOrderInfoVO.java

@@ -3,6 +3,7 @@ package com.zsElectric.boot.business.model.vo;
 import java.io.Serial;
 import java.io.Serializable;
 import java.time.LocalDateTime;
+
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
@@ -17,7 +18,7 @@ import java.math.BigDecimal;
  */
 @Getter
 @Setter
-@Schema( description = "充电订单信息视图对象")
+@Schema(description = "充电订单信息视图对象")
 public class ChargeOrderInfoVO implements Serializable {
 
     @Serial
@@ -77,6 +78,8 @@ public class ChargeOrderInfoVO implements Serializable {
     private BigDecimal maspRealAmount;
     @Schema(description = "需要补缴的总金额")
     private BigDecimal totalMaspMoney;
+    @Schema(description = "已补缴的金额")
+    private BigDecimal alreadyMaspMoney;
     @Schema(description = "补缴状态  0.无需补缴  1.待补缴  2.已补缴")
     private Integer maspStatus;
     @Schema(description = "补缴时间")

+ 0 - 5
src/main/java/com/zsElectric/boot/business/model/vo/applet/AppletUserInfoVO.java

@@ -46,11 +46,6 @@ public class AppletUserInfoVO implements Serializable {
      */
     private BigDecimal balance;
 
-    /**
-     * 欠款金额
-     */
-    private BigDecimal amountOwed;
-
     /**
      * 企业ID
      */

+ 13 - 0
src/main/java/com/zsElectric/boot/business/service/UserAccountService.java

@@ -55,4 +55,17 @@ public interface UserAccountService extends IService<UserAccount> {
      */
     boolean deleteUserAccounts(String ids);
 
+    /**
+     * 更新账户余额并记录日志
+     *
+     * @param userId 用户ID
+     * @param changeAmount 变更金额(正数为增加,负数为减少)
+     * @param changeType 变更类型(1-增加,2-减少)
+     * @param changeNote 变更备注
+     * @param changeId 变更记录编号(订单号或充电订单号)
+     * @return 更新后的用户账户信息
+     */
+    UserAccount updateAccountBalanceAndLog(Long userId, java.math.BigDecimal changeAmount,
+                                          Integer changeType, String changeNote, Long changeId);
+
 }

+ 3 - 3
src/main/java/com/zsElectric/boot/business/service/UserInfoService.java

@@ -57,12 +57,12 @@ public interface UserInfoService extends IService<UserInfo> {
     boolean deleteUserInfos(String ids);
 
     /**
-     * 根据手机号获取用户信息
+     * 根据openid获取用户信息
      *
-     * @param phone 手机号
+     * @param openid 手机号
      * @return 用户信息,不存在返回null
      */
-    UserInfo getUserInfoByPhone(String phone);
+    UserInfo getUserInfoByOpenid(String openid);
 
     /**
      * 根据手机号注册或更新用户(小程序用)

+ 2 - 0
src/main/java/com/zsElectric/boot/business/service/UserOrderInfoService.java

@@ -72,4 +72,6 @@ public interface UserOrderInfoService extends IService<UserOrderInfo> {
     String cancelOrder(String orderId);
 
     String refundOrder() throws Exception;
+
+    Map<String, Object> refundCallback(HttpServletRequest request, HttpServletResponse response);
 }

+ 51 - 0
src/main/java/com/zsElectric/boot/business/service/impl/UserAccountServiceImpl.java

@@ -1,5 +1,8 @@
 package com.zsElectric.boot.business.service.impl;
 
+import com.zsElectric.boot.business.mapper.UserAccountLogMapper;
+import com.zsElectric.boot.business.model.entity.UserAccountLog;
+import com.zsElectric.boot.common.constant.SystemConstants;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -13,6 +16,7 @@ import com.zsElectric.boot.business.model.query.UserAccountQuery;
 import com.zsElectric.boot.business.model.vo.UserAccountVO;
 import com.zsElectric.boot.business.converter.UserAccountConverter;
 
+import java.math.BigDecimal;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -31,6 +35,8 @@ import cn.hutool.core.util.StrUtil;
 public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {
 
     private final UserAccountConverter userAccountConverter;
+    
+    private final UserAccountLogMapper userAccountLogMapper;
 
     /**
     * 获取个人账户分页列表
@@ -99,5 +105,50 @@ public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserA
                 .toList();
         return this.removeByIds(idList);
     }
+    /**
+     * 更新账户余额并记录日志
+     *
+     * @param userId 用户ID
+     * @param changeAmount 变更金额(正数为增加,负数为减少)
+     * @param changeType 变更类型(1-增加,2-减少)
+     * @param changeNote 变更备注
+     * @param changeId 变更记录编号(订单号或充电订单号)
+     * @return 更新后的用户账户信息
+     */
+    @Override
+    public UserAccount updateAccountBalanceAndLog(Long userId, BigDecimal changeAmount,
+                                                  Integer changeType, String changeNote, Long changeId) {
+        // 查询用户账户
+        UserAccount userAccount = this.getById(userId);
+        
+        // 创建账户变动日志
+        UserAccountLog accountLog = new UserAccountLog();
+        accountLog.setBeforeBalance(userAccount.getBalance());
+        
+        // 计算变更后余额
+        BigDecimal finalBalance;
+        if (SystemConstants.CHANGE_TYPE_ADD.equals(changeType)) {
+            // 增加余额
+            finalBalance = userAccount.getBalance().add(changeAmount);
+        } else {
+            // 减少余额
+            finalBalance = userAccount.getBalance().subtract(changeAmount);
+        }
+        
+        // 更新账户余额
+        userAccount.setBalance(finalBalance);
+        this.updateById(userAccount);
+        
+        // 保存变动日志
+        accountLog.setUserId(userId);
+        accountLog.setChangeType(changeType);
+        accountLog.setChangeNote(changeNote);
+        accountLog.setChangeId(changeId);
+        accountLog.setAccountType(SystemConstants.ACCOUNT_TYPE_PERSONAL);
+        accountLog.setChangeBalance(finalBalance);
+        userAccountLogMapper.insert(accountLog);
+        
+        return userAccount;
+    }
 
 }

+ 23 - 12
src/main/java/com/zsElectric/boot/business/service/impl/UserInfoServiceImpl.java

@@ -1,6 +1,8 @@
 package com.zsElectric.boot.business.service.impl;
 
 import cn.hutool.core.util.ObjectUtil;
+import com.zsElectric.boot.business.mapper.UserAccountMapper;
+import com.zsElectric.boot.business.model.entity.UserAccount;
 import com.zsElectric.boot.business.model.vo.applet.AppletUserInfoVO;
 import com.zsElectric.boot.security.util.SecurityUtils;
 import lombok.RequiredArgsConstructor;
@@ -18,6 +20,7 @@ import com.zsElectric.boot.business.model.query.UserInfoQuery;
 import com.zsElectric.boot.business.model.vo.UserInfoVO;
 import com.zsElectric.boot.business.converter.UserInfoConverter;
 
+import java.math.BigDecimal;
 import java.util.Arrays;
 import java.util.List;
 
@@ -37,6 +40,8 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
 
     private final UserInfoConverter userInfoConverter;
 
+    private final UserAccountMapper userAccountMapper;
+
     /**
     * 获取个人用户信息分页列表
     *
@@ -124,19 +129,19 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
     }
 
     /**
-     * 根据手机号获取用户信息
+     * 根据openid获取用户信息
      *
-     * @param phone 手机号
+     * @param openid 手机号
      * @return 用户信息,不存在返回null
      */
     @Override
-    public UserInfo getUserInfoByPhone(String phone) {
-        if (StrUtil.isBlank(phone)) {
+    public UserInfo getUserInfoByOpenid(String openid) {
+        if (StrUtil.isBlank(openid)) {
             return null;
         }
         return this.getOne(
                 new LambdaQueryWrapper<UserInfo>()
-                        .eq(UserInfo::getPhone, phone)
+                        .eq(UserInfo::getOpenid, openid)
         );
     }
 
@@ -157,15 +162,15 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
         }
 
         // 查询用户是否已存在
-        UserInfo existingUser = getUserInfoByPhone(phone);
+        UserInfo existingUser = getUserInfoByOpenid(phone);
 
         if (existingUser != null) {
-            log.info("用户已存在,ID: {}, 手机号: {}", existingUser.getId(), phone);
+            log.info("用户已存在,ID: {}, 手机号: {},openid: {}", existingUser.getId(), phone,openId);
             
-            // 如果提供了openId且与现有openId不同,则更新
-            if (StrUtil.isNotBlank(openId) && !openId.equals(existingUser.getOpenid())) {
-                log.info("更新用户openId,ID: {}", existingUser.getId());
-                existingUser.setOpenid(openId);
+            // 如果提供了phone且与现有phone不同,则更新
+            if (StrUtil.isNotBlank(phone) && !openId.equals(existingUser.getOpenid())) {
+                log.info("更新用户phone,ID: {}", existingUser.getId());
+                existingUser.setPhone(phone);
                 this.updateById(existingUser);
             }
             
@@ -185,8 +190,14 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
             log.error("保存用户失败,手机号: {}", phone);
             return null;
         }
-        
         log.info("用户创建成功,ID: {}, 手机号: {}", newUser.getId(), phone);
+
+        //创建用户账户
+        UserAccount userAccount = new UserAccount();
+        userAccount.setUserId(newUser.getId());
+        userAccount.setBalance(BigDecimal.ZERO);
+        userAccountMapper.insert(userAccount);
+
         return newUser;
     }
 

+ 331 - 66
src/main/java/com/zsElectric/boot/business/service/impl/UserOrderInfoServiceImpl.java

@@ -4,8 +4,12 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSON;
+import com.aliyun.oss.ServiceException;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.google.gson.Gson;
 import com.google.gson.JsonObject;
+import com.zsElectric.boot.business.UserRefundsOrderInfo;
 import com.zsElectric.boot.business.mapper.*;
 import com.zsElectric.boot.business.model.entity.*;
 import com.zsElectric.boot.business.model.form.applet.LevelOrderForm;
@@ -14,15 +18,15 @@ import com.zsElectric.boot.business.model.vo.UserInfoVO;
 import com.zsElectric.boot.business.service.*;
 import com.zsElectric.boot.common.constant.SystemConstants;
 import com.zsElectric.boot.core.exception.BusinessException;
-import com.zsElectric.boot.core.pay.WXPayUtility;
-import com.zsElectric.boot.core.pay.WechatConstants;
-import com.zsElectric.boot.core.pay.WechatPayV3Utils;
-import com.zsElectric.boot.core.pay.WechatUrlConstants;
+import com.zsElectric.boot.core.pay.*;
 import com.zsElectric.boot.security.util.SecurityUtils;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import netscape.javascript.JSObject;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -65,12 +69,14 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
     private final UserAccountMapper userAccountMapper;
 
-    private final UserAccountLogMapper userAccountLogMapper;
+    private final UserAccountService userAccountService;
 
     private final WechatPayV3Utils wechatPayV3Utils;
 
     private final ChargeOrderInfoMapper chargeOrderInfoMapper;
 
+    private final UserRefundsOrderInfoMapper userRefundsOrderInfoMapper;
+
     // 声明一个可重入锁
     private final ReentrantLock lock = new ReentrantLock();
 
@@ -144,6 +150,7 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
     /**
      * 创建订单
+     *
      * @param levelOrderForm
      * @return
      */
@@ -151,7 +158,7 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
     public UserPayForm createOrder(LevelOrderForm levelOrderForm) {
         Long userId = SecurityUtils.getUserId();
         String userOpenId = userInfoMapper.getAppletUserInfo(userId).getOpenid();
-        String orderNo = createOrderNo("SP",userId);
+        String orderNo = createOrderNo("SP", userId);
         //创建订单
         UserOrderInfo orderInfo = new UserOrderInfo();
         orderInfo.setUserId(userId);
@@ -168,13 +175,14 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
         //查询档位
         RechargeLevel level = rechargeLevelMapper.selectById(levelOrderForm.getLevelId());
 
-        Map<String, Object> result = payment(userOpenId,orderNo,level.getMoney());
+        Map<String, Object> result = payment(userOpenId, orderNo, level.getMoney());
         payForm.setParams(result);
         return payForm;
     }
 
     /**
      * 支付订单
+     *
      * @param orderId
      * @return
      */
@@ -188,13 +196,14 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
         //查询档位
         RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId());
 
-        Map<String, Object> result = payment(orderInfo.getOpenid(),orderInfo.getOrderNo(),level.getMoney());
+        Map<String, Object> result = payment(orderInfo.getOpenid(), orderInfo.getOrderNo(), level.getMoney());
         payForm.setParams(result);
         return payForm;
     }
 
     /**
      * 订单查询
+     *
      * @param orderNo
      * @return
      */
@@ -213,7 +222,9 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
             if (ObjectUtil.isNotEmpty(orderInfo) && Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) {
                 String transactionId = res.get("transaction_id").getAsString();
                 LocalDateTime payTime = LocalDateTime.now();
-                BigDecimal payMoney = new BigDecimal(res.get("amount").getAsJsonObject().get("total").getAsString());
+                // 微信返回的金额单位是分,需要转换为元
+                BigDecimal payMoney = new BigDecimal(res.get("amount").getAsJsonObject().get("total").getAsString())
+                        .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
 
                 //执行业务操作
                 this.successPayOrder(orderInfo, transactionId, payTime, payMoney);
@@ -233,6 +244,7 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
     /**
      * 微信支付回调
+     *
      * @param request
      * @param response
      * @return
@@ -265,7 +277,9 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
                     if (Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) {
                         String transactionId = res.get("transaction_id").getAsString();
                         LocalDateTime payTime = LocalDateTime.now();
-                        BigDecimal payMoney = new BigDecimal(res.get("amount").getAsJsonObject().get("total").getAsString());
+                        // 微信返回的金额单位是分,需要转换为元
+                        BigDecimal payMoney = new BigDecimal(res.get("amount").getAsJsonObject().get("total").getAsString())
+                                .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
 
                         //执行业务操作
                         this.successPayOrder(orderInfo, transactionId, payTime, payMoney);
@@ -279,10 +293,10 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
                 result.put("code", "FAIL");
                 result.put("message", "失败");
                 return result;
-            }finally {
+            } finally {
                 lock.unlock();
             }
-        }else {
+        } else {
             result.put("code", "FAIL");
             result.put("message", "失败");
             return result;
@@ -291,6 +305,7 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
     /**
      * 支付成功业务处理
+     *
      * @param orderInfo
      * @param transactionId
      * @param payTime
@@ -298,7 +313,7 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
      * @throws Exception
      */
     @Transactional(rollbackFor = Exception.class)
-    protected void successPayOrder(UserOrderInfo orderInfo, String transactionId, LocalDateTime payTime, BigDecimal orderMoney) throws Exception{
+    protected void successPayOrder(UserOrderInfo orderInfo, String transactionId, LocalDateTime payTime, BigDecimal orderMoney) throws Exception {
         //修改订单状态为已支付
         orderInfo.setOrderStatus(SystemConstants.STATUS_TWO);
         orderInfo.setPayTime(payTime);
@@ -308,30 +323,19 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
         //账户变动及日志记录
         Long userId = orderInfo.getUserId();
-        UserAccount userAccount = userAccountMapper.selectById(userId);
-        //账户变动日志
-        UserAccountLog accountLog = new UserAccountLog();
-        accountLog.setBeforeBalance(userAccount.getBalance());
-        //增加
-        BigDecimal finalBalance = BigDecimal.ZERO;
-        finalBalance = userAccount.getBalance().add(orderMoney);
-        userAccount.setBalance(finalBalance);
-        userAccountMapper.updateById(userAccount);
-        //日志
-        accountLog.setUserId(userId);
-        accountLog.setChangeType(SystemConstants.CHANGE_TYPE_ADD);
-        accountLog.setChangeNote(SystemConstants.ACCOUNT_LOG_PAY_NOTE);
-        accountLog.setChangeId(orderInfo.getId());
-        accountLog.setAccountType(SystemConstants.ACCOUNT_TYPE_PERSONAL);
-        accountLog.setChangeBalance(finalBalance);
-        userAccountLogMapper.insert(accountLog);
-
-        //平订单超充金额
-        orderBackTax(userAccount);
+        UserAccount userAccount = userAccountService.updateAccountBalanceAndLog(
+                userId,
+                orderMoney,
+                SystemConstants.CHANGE_TYPE_ADD,
+                SystemConstants.ACCOUNT_LOG_PAY_NOTE,
+                orderInfo.getId()
+        );
+
+        //平订单超充金额(使用更新后的账户信息)
+        orderBackTax(userId, userAccount);
     }
 
-    private void orderBackTax(UserAccount userAccount) {
-        Long userId = SecurityUtils.getUserId();
+    private void orderBackTax(Long userId, UserAccount userAccount) {
         //查询超充订单
         List<ChargeOrderInfo> chargeOrderInfoList = chargeOrderInfoMapper.selectList(Wrappers.<ChargeOrderInfo>lambdaQuery()
                 .eq(ChargeOrderInfo::getUserId, userId)
@@ -339,10 +343,50 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
                 .orderByAsc(ChargeOrderInfo::getCreateTime)
         );
         if (CollUtil.isNotEmpty(chargeOrderInfoList)) {
+            //余额减去超充金额(使用更新后的余额)
             BigDecimal balance = userAccount.getBalance();
             for (ChargeOrderInfo chargeOrderInfo : chargeOrderInfoList) {
-                balance = balance.subtract(chargeOrderInfo.getTotalMaspMoney());
-                //todo
+
+                if (balance.compareTo(BigDecimal.ZERO) > 0) {
+                    BigDecimal deductAmount; // 本次扣除金额
+
+                    if (balance.compareTo(chargeOrderInfo.getTotalMaspMoney()) < 0) {
+                        //余额不足,部分补缴
+                        deductAmount = balance;
+                        chargeOrderInfo.setAlreadyMaspMoney(deductAmount);
+                        chargeOrderInfo.setMaspDesc("部分补缴");
+                        chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO);
+                        chargeOrderInfoMapper.updateById(chargeOrderInfo);
+
+                        //账户变动及日志记录
+                        userAccountService.updateAccountBalanceAndLog(
+                                userId,
+                                deductAmount.negate(),
+                                SystemConstants.CHANGE_TYPE_REDUCE,
+                                SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE,
+                                chargeOrderInfo.getId()
+                        );
+
+                        balance = BigDecimal.ZERO;
+                        continue;
+                    }
+                    //余额足够,完成补缴
+                    deductAmount = chargeOrderInfo.getTotalMaspMoney();
+                    balance = balance.subtract(deductAmount);
+                    chargeOrderInfo.setAlreadyMaspMoney(deductAmount);
+                    chargeOrderInfo.setMaspDesc("完成补缴");
+                    chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO);
+                    chargeOrderInfoMapper.updateById(chargeOrderInfo);
+
+                    //账户变动及日志记录
+                    userAccountService.updateAccountBalanceAndLog(
+                            userId,
+                            deductAmount.negate(),
+                            SystemConstants.CHANGE_TYPE_REDUCE,
+                            SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE,
+                            chargeOrderInfo.getId()
+                    );
+                }
             }
         }
 
@@ -350,6 +394,7 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
     /**
      * 取消订单
+     *
      * @param orderId
      * @return
      */
@@ -369,6 +414,7 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
     /**
      * 账户退款
+     *
      * @return
      */
     @Override
@@ -377,27 +423,216 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
         //查询账户余额
         UserAccount userAccount = userAccountMapper.selectOne(Wrappers.<UserAccount>lambdaQuery().eq(UserAccount::getUserId, SecurityUtils.getUserId()).last("limit 1"));
-        if(userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0){
+        if (userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0) {
             throw new BusinessException("账户余额为 0");
         }
         BigDecimal refundMoney = userAccount.getBalance();
 
-        //账户变动及日志记录
-        UserAccountLog accountLog = new UserAccountLog();
-        accountLog.setBeforeBalance(userAccount.getBalance());
-        //账户余额清空
-        userAccount.setBalance(BigDecimal.ZERO);
-        userAccountMapper.updateById(userAccount);
-        //日志
-        accountLog.setUserId(SecurityUtils.getUserId());
-        accountLog.setChangeType(SystemConstants.CHANGE_TYPE_REDUCE);
-        accountLog.setChangeNote(SystemConstants.ACCOUNT_LOG_REFUND_NOTE);
-        //accountLog.setChangeId();
-        accountLog.setAccountType(SystemConstants.ACCOUNT_TYPE_PERSONAL);
-        accountLog.setChangeBalance(BigDecimal.ZERO);
-        userAccountLogMapper.insert(accountLog);
-
-        return "";
+        //查询一年内已支付的所有券订单
+        List<UserOrderInfo> userOrderInfoList = baseMapper.selectList(Wrappers.<UserOrderInfo>lambdaQuery()
+                .eq(UserOrderInfo::getOrderStatus, SystemConstants.STATUS_TWO)
+                .between(UserOrderInfo::getCreateTime, LocalDateTime.now().minusYears(1), LocalDateTime.now())
+        );
+
+        if (CollUtil.isEmpty(userOrderInfoList)) {
+            log.info("当前用户一年内未支付任何券订单,无法进行退款操作!");
+            throw new BusinessException("无法进行退款操作,请联系客服处理!");
+        }
+        for (UserOrderInfo userOrderInfo : userOrderInfoList) {
+            if(refundMoney.compareTo(BigDecimal.ZERO) == 0){
+                break;
+            }
+            if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) > 0) {
+                //退款金额大于订单金额,则直接退退款金额
+                refundOrder(userOrderInfo,refundMoney, "账户退款", SystemConstants.STATUS_ONE);
+                //账户变动及日志记录
+                userAccountService.updateAccountBalanceAndLog(
+                        SecurityUtils.getUserId(),
+                        refundMoney,
+                        SystemConstants.CHANGE_TYPE_REDUCE,
+                        SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
+                        userOrderInfo.getId()
+                );
+                //修改订单状态
+                userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
+                this.updateById(userOrderInfo);
+                refundMoney = BigDecimal.ZERO;
+                break;
+            }
+            if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) < 0) {
+                //退款金额小于订单金额,则先退订单金额
+                refundOrder(userOrderInfo,userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()), "账户退款", SystemConstants.STATUS_ONE);
+                //账户变动及日志记录
+                userAccountService.updateAccountBalanceAndLog(
+                        SecurityUtils.getUserId(),
+                        userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()),
+                        SystemConstants.CHANGE_TYPE_REDUCE,
+                        SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
+                        userOrderInfo.getId()
+                );
+                //修改订单状态
+                userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
+                this.updateById(userOrderInfo);
+                refundMoney = refundMoney.subtract(userOrderInfo.getOrderMoney());
+            }
+        }
+        return "账户退款,预计3个工作日内分一笔或多笔退还!如未收到,请联系客服!";
+    }
+
+    public void refundOrder(UserOrderInfo userOrderInfo, BigDecimal refundAmount, String reason, Integer type) {
+        log.info("进入退款接口------>");
+        log.info("执行操作的 原支付交易对应的商户订单号:{}", userOrderInfo.getOrderNo());
+
+        //退款单号
+        String out_refund_no = createOrderNo("TK",userOrderInfo.getId());
+        // 创建退款订单
+        UserRefundsOrderInfo userRefundsOrderInfo = new UserRefundsOrderInfo();
+        userRefundsOrderInfo.setOrderId(userOrderInfo.getId());
+        userRefundsOrderInfo.setOrderNo(userOrderInfo.getOrderNo());
+        userRefundsOrderInfo.setOutRefundNo(out_refund_no);
+        userRefundsOrderInfo.setReason(reason);
+        userRefundsOrderInfo.setAmount(refundAmount);
+        userRefundsOrderInfo.setType(type);
+        userRefundsOrderInfo.setCreateTime(LocalDateTime.now());
+
+        Map<String,Object> params = new HashMap<String,Object>();
+        params.put("transaction_id", userOrderInfo.getTransactionId());
+        params.put("out_trade_no", userOrderInfo.getOrderNo());//商户订单号
+        params.put("out_refund_no", out_refund_no);//商户退款单号
+        params.put("reason", reason);//退款原因
+        params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY);//退款通知
+
+        Map<String,Object> amount = new HashMap<String,Object>();
+        amount.put("refund", amount_fee(refundAmount));//退款金额
+        amount.put("currency", "CNY");
+        amount.put("total", amount_fee(userOrderInfo.getOrderMoney()));//原订单金额
+        params.put("amount", amount);
+        // 执行请求POST 请求发送到微信退款接口
+        Gson gson = new Gson();
+        JsonObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, gson.toJsonTree(params).getAsJsonObject());
+
+        log.info("最终拿到的微信支付通知数据:" + res);
+
+        final String status = res.get("status").getAsString();
+        switch (status) {
+            case "SUCCESS":
+                log.info("订单:{},退款成功!原因:{}", userOrderInfo.getOrderNo(), reason);
+                //修改订单状态
+                userOrderInfo.setOrderStatus(SystemConstants.STATUS_FIVE);
+                userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount));
+                userOrderInfo.setRefundTime(LocalDateTime.now());
+                this.updateById(userOrderInfo);
+                break;
+            case "CLOSED":
+                log.info("退款关闭");
+                break;
+            case "PROCESSING":
+                log.info("退款处理中");
+                //修改订单状态
+                userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount));
+                this.updateById(userOrderInfo);
+                break;
+            case "ABNORMAL":
+                log.info("订单:{},退款异常", userOrderInfo.getOrderNo());
+                break;
+        }
+        userRefundsOrderInfo.setRefundId(res.get("refund_id").getAsString());
+        userRefundsOrderInfo.setNotifyRequest(res.toString());
+        userRefundsOrderInfo.setTransactionId(res.get("transaction_id").getAsString());
+        userRefundsOrderInfo.setAcceptedTime(LocalDateTime.now());
+        userRefundsOrderInfoMapper.insert(userRefundsOrderInfo);
+    }
+
+    @Override
+    public Map<String, Object> refundCallback(HttpServletRequest request, HttpServletResponse response) {
+
+        WechatRefundCallback refundCallback = new WechatRefundCallback() {
+            @Override
+            public void success(WechatCallbackRefundData refundData) {
+                log.info("微信支付退款成功!");
+
+                UserOrderInfo userOrderInfo = baseMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, refundData.getOrderNo()));
+                userOrderInfo.setOrderStatus(SystemConstants.STATUS_FIVE);
+                userOrderInfo.setRefundTime(refundData.getSuccessTime());
+                baseMapper.updateById(userOrderInfo);
+
+                UserRefundsOrderInfo userRefundsOrderInfo = userRefundsOrderInfoMapper.selectOne(Wrappers.<UserRefundsOrderInfo>lambdaQuery()
+                        .eq(UserRefundsOrderInfo::getOrderId, userOrderInfo.getId())
+                        .eq(UserRefundsOrderInfo::getRefundId, refundData.getTransactionRefundId())
+                        .last("limit 1")
+                );
+                userRefundsOrderInfo.setStatus(refundData.getStatus());
+                userRefundsOrderInfo.setSuccessTime(refundData.getSuccessTime());
+                userRefundsOrderInfoMapper.updateById(userRefundsOrderInfo);
+
+            }
+
+            @Override
+            public void fail(WechatCallbackRefundData refundData) {
+                log.info("微信支付退款失败!");
+
+                UserOrderInfo userOrderInfo = baseMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, refundData.getOrderNo()));
+
+                UserRefundsOrderInfo userRefundsOrderInfo = userRefundsOrderInfoMapper.selectOne(Wrappers.<UserRefundsOrderInfo>lambdaQuery()
+                        .eq(UserRefundsOrderInfo::getOrderId, userOrderInfo.getId())
+                        .eq(UserRefundsOrderInfo::getRefundId, refundData.getTransactionRefundId())
+                        .last("limit 1")
+                );
+                userRefundsOrderInfo.setStatus(refundData.getStatus());
+                userRefundsOrderInfo.setSuccessTime(refundData.getSuccessTime());
+                userRefundsOrderInfoMapper.updateById(userRefundsOrderInfo);
+
+            }
+        };
+
+        Map<String, Object> result = new HashMap<>();
+        if (lock.tryLock()) {
+            // 2.签名验证
+            //验签及解析返回数据
+            JsonObject res = wechatPayV3Utils.getCallbackData(request);
+            if (res == null) {
+                result.put("code", "FAIL");
+                result.put("message", "失败");
+                return result;
+            }
+            log.info("最终拿到的微信支付通知数据:" + res);
+
+            // 4.封装微信返回的数据
+            WechatCallbackRefundData refundData = getRefundCallbackData(res);
+            if ("SUCCESS".equals(refundData.getStatus())) {
+                refundCallback.success(refundData);
+            } else {
+                // 特殊情况退款失败业务处理,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款
+                refundCallback.fail(refundData);
+            }
+            // 5.成功应答
+            response.setStatus(200);
+            result.put("code", "SUCCESS");
+            result.put("message", "成功");
+        } else {
+            result.put("code", "FAIL");
+            result.put("message", "失败");
+        }
+        return result;
+    }
+
+    private static WechatCallbackRefundData getRefundCallbackData(JsonObject res) {
+        WechatCallbackRefundData refundData = new WechatCallbackRefundData();
+        String successTime = res.get("success_time").getAsString();
+        if (StringUtils.isNoneBlank(successTime)) {
+            refundData.setSuccessTime(successTime);
+        }
+        refundData.setOrderNo(res.get("out_trade_no").getAsString());
+        refundData.setRefundId(res.get("out_refund_no").getAsString());
+        refundData.setTransactionId(res.get("transaction_id").getAsString());
+        refundData.setTransactionRefundId(res.get("refund_id").getAsString());
+        refundData.setChannel(res.get("channel").getAsString());
+        final String status = res.get("refund_status").getAsString();
+        refundData.setStatus(status);
+        String refundMoney = res.getAsJsonObject("amount").get("refund").getAsString();
+        refundData.setRefundMoney(new BigDecimal(refundMoney).movePointLeft(2));
+        log.info("refundData:{}", refundData);
+        return refundData;
     }
 
     /**
@@ -417,19 +652,20 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
 
     /**
      * 构建支付表单返回给前端支撑JsApi支付调用
+     *
      * @param openId
      * @param orderNo
      * @param amount
      * @return
      */
-    private Map<String, Object> payment(String openId, String orderNo,BigDecimal amount) {
+    private Map<String, Object> payment(String openId, String orderNo, BigDecimal amount) {
         //15分钟超时限制
         Calendar calendar = Calendar.getInstance();
         calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 15);
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
         //构建微信支付参数
         Map<String, Object> params = new HashMap<>();
-        params.put("appid", WechatConstants.WECHAT_MP_APPID); //小程序appid
+        params.put("appid", WechatConstants.WECHAT_APPID); //小程序appid
         params.put("mchid", WechatConstants.WECHAT_MCH_ID); //商户号
         params.put("description", "订单业务"); //商品描述
         params.put("out_trade_no", orderNo); //商户订单号
@@ -451,7 +687,45 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
         payer.put("openid", openId);
         params.put("payer", payer);
 
-        return params;
+        //小程序支付拉起参数
+        Gson gson = new Gson();
+        return wechatPay(gson.toJsonTree(params).getAsJsonObject());
+    }
+
+
+    /**
+     * 小程序支付拉起
+     *
+     * @param params
+     * @return
+     * @throws ServiceException
+     */
+    public Map<String, Object> wechatPay(JsonObject params) throws ServiceException {
+
+        //发起请求
+        JsonObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_JSAPI, params);
+        log.info("wechatPay res:{}", res.toString());
+        if (StrUtil.isEmpty(res.get("prepay_id").getAsString())) {
+            throw new ServiceException("支付发起失败");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        //返回给小程序拉起微信支付的参数
+        Map<String, Object> result = new HashMap<>();
+        result.put("appId", WechatConstants.WECHAT_APPID); //小程序appid
+        sb.append(result.get("appId")).append("\n");
+        result.put("timeStamp", (new Date().getTime() / 1000) + ""); //时间戳
+        sb.append(result.get("timeStamp")).append("\n");
+        result.put("nonceStr", RandomStringUtils.randomAlphanumeric(32)); //32位随机字符串
+        sb.append(result.get("nonceStr")).append("\n");
+        result.put("package", "prepay_id=" + res.get("prepay_id").getAsString()); //预支付id 格式为 prepay_id=xxx
+        sb.append(result.get("package")).append("\n");
+        //签名
+        result.put("paySign", wechatPayV3Utils.signRSA(sb.toString()));
+        result.put("signType", "RSA"); //加密方式 固定RSA
+        result.put("out_trade_no", params.get("out_trade_no").getAsString()); //商户订单号 此参数不是小程序拉起支付所需的参数 因此不参与签名
+
+        return result;
     }
 
     /**
@@ -492,13 +766,4 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
         return money + "";
     }
 
-    /**
-     * 分转元,转换为bigDecimal在转成double
-     *
-     * @return
-     */
-    public static double changeF2Y3(int price) {
-        return BigDecimal.valueOf(Long.valueOf(price)).divide(new BigDecimal("100")).doubleValue();
-    }
-
 }

+ 5 - 0
src/main/java/com/zsElectric/boot/common/constant/SystemConstants.java

@@ -40,6 +40,10 @@ public interface SystemConstants {
 
     Integer STATUS_THREE = 3;
 
+    Integer STATUS_FOUR = 4;
+
+    Integer STATUS_FIVE = 5;
+
     /**
      * 充值记录描述
      */
@@ -58,4 +62,5 @@ public interface SystemConstants {
      */
     Integer ACCOUNT_TYPE_PERSONAL = 1;
     Integer ACCOUNT_TYPE_COMPANY = 2;
+
 }

+ 75 - 0
src/main/java/com/zsElectric/boot/core/pay/WechatCallbackRefundData.java

@@ -0,0 +1,75 @@
+package com.zsElectric.boot.core.pay;
+
+import cn.hutool.core.date.DateUtil;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Data
+@Slf4j
+public class WechatCallbackRefundData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 商户订单号
+     */
+    private String orderNo;
+
+
+    /**
+     * 商户退款单号,out_refund_no
+     */
+    private String refundId;
+
+    /**
+     * 微信支付系统生成的订单号
+     */
+    private String transactionId;
+
+    /**
+     * 微信支付系统生成的退款订单号
+     */
+    private String transactionRefundId;
+
+    /**
+     * 退款渠道
+     * ORIGINAL:原路退款
+     * BALANCE:退回到余额
+     * OTHER_BALANCE:原账户异常退到其他余额账户
+     * OTHER_BANKCARD:原银行卡异常退到其他银行卡
+     */
+    private String 	channel;
+
+    /**
+     * 退款成功时间
+     * 当前退款成功时才有此返回值
+     */
+    private LocalDateTime successTime;
+
+    /**
+     * 退款状态
+     * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。
+     * SUCCESS:退款成功
+     * CLOSED:退款关闭
+     * PROCESSING:退款处理中
+     * ABNORMAL:退款异常
+     */
+    private String status;
+
+    /**
+     * 退款金额
+     */
+    private BigDecimal refundMoney;
+
+    public void setSuccessTime(String successTime) {
+        // Hutool工具包的方法,自动识别一些常用格式的日期字符串
+        this.successTime = DateUtil.parseLocalDateTime(successTime);
+    }
+}

+ 3 - 3
src/main/java/com/zsElectric/boot/core/pay/WechatConstants.java

@@ -8,7 +8,7 @@ package com.zsElectric.boot.core.pay;
 public interface WechatConstants {
 
     //微信支付商户号
-    String WECHAT_MCH_ID = "1523499681";
+    String WECHAT_MCH_ID = "1726610149";
 
     //微信商户平台v3密钥
     String WECHAT_MCH_SECRET_V3 = "b63646f9b01e573c5bed89dc0f21039e";
@@ -20,9 +20,9 @@ public interface WechatConstants {
     String WECHAT_MCH_PRIVATE_KEY = "";
 
     //微信小程序appid
-    String WECHAT_MP_APPID = "wx9b0396a7507e3d66";
+    String WECHAT_APPID = "wx52bee24aece42dd9";
 
     //微信小程序密钥
-    String WECHAT_MP_SECRET = "22b78f76ab5282030ffa08208b223efd";
+    String WECHAT_SECRET = "9c1dd607955f42ab4fabfd898839eb88";
 
 }

+ 18 - 0
src/main/java/com/zsElectric/boot/core/pay/WechatRefundCallback.java

@@ -0,0 +1,18 @@
+package com.zsElectric.boot.core.pay;
+
+/**
+ * 退款处理接口,为了防止项目开发人员,不手动判断退款失败的情况
+ * 退款失败:退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款
+ */
+public interface WechatRefundCallback {
+
+    /**
+     * 退款成功处理情况
+     */
+    void success(WechatCallbackRefundData refundData);
+
+    /**
+     * 退款失败处理情况
+     */
+    void fail(WechatCallbackRefundData refundData);
+}

+ 13 - 1
src/main/java/com/zsElectric/boot/security/model/WxMiniAppPhoneCodeAuthenticationToken.java

@@ -17,14 +17,17 @@ public class WxMiniAppPhoneCodeAuthenticationToken extends AbstractAuthenticatio
     private static final long serialVersionUID = 623L;
     
     private final Object principal; // phoneCode
+    private String code; // 微信登录code,用于换取openid
 
     /**
      * 微信小程序手机号Code认证Token (未认证)
      *
+     * @param code 微信登录code,用于换取openid
      * @param phoneCode 微信手机号code
      */
-    public WxMiniAppPhoneCodeAuthenticationToken(String phoneCode) {
+    public WxMiniAppPhoneCodeAuthenticationToken(String code, String phoneCode) {
         super(null);
+        this.code = code;
         this.principal = phoneCode;
         this.setAuthenticated(false);
     }
@@ -61,4 +64,13 @@ public class WxMiniAppPhoneCodeAuthenticationToken extends AbstractAuthenticatio
     public Object getPrincipal() {
         return this.principal;
     }
+
+    /**
+     * 获取微信登录code
+     * 
+     * @return 微信登录code
+     */
+    public String getCode() {
+        return code;
+    }
 }

+ 23 - 10
src/main/java/com/zsElectric/boot/security/provider/WxMiniAppPhoneCodeAuthenticationProvider.java

@@ -35,15 +35,28 @@ public class WxMiniAppPhoneCodeAuthenticationProvider implements AuthenticationP
     @Override
     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
         WxMiniAppPhoneCodeAuthenticationToken authenticationToken = (WxMiniAppPhoneCodeAuthenticationToken) authentication;
+        String code = authenticationToken.getCode();
         String phoneCode = (String) authenticationToken.getPrincipal();
 
-        // 1. 通过phoneCode获取手机号信息(新版接口)
-        WxMaPhoneNumberInfo phoneNumberInfo;
+        // 1. 通过code获取openid
+        String openId = null;
+        try {
+            cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
+            openId = sessionInfo.getOpenid();
+            log.info("通过code获取到openid: {}", openId);
+        } catch (me.chanjar.weixin.common.error.WxErrorException e) {
+            log.error("获取微信openid失败", e);
+            // openid获取失败不阻断登录,继续执行
+            throw new CredentialsExpiredException("获取openid失败,code无效或已过期");
+        }
+
+        // 2. 通过phoneCode获取手机号信息(新版接口)
+        cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo phoneNumberInfo;
         try {
             phoneNumberInfo = wxMaService.getUserService().getPhoneNoInfo(phoneCode);
-        } catch (WxErrorException e) {
+        } catch (me.chanjar.weixin.common.error.WxErrorException e) {
             log.error("获取微信手机号失败", e);
-            throw new CredentialsExpiredException("获取手机号失败,code无效或已过期");
+            throw new CredentialsExpiredException("获取手机号失败,phoneCode无效或已过期");
         }
 
         if (phoneNumberInfo == null || StrUtil.isBlank(phoneNumberInfo.getPhoneNumber())) {
@@ -51,18 +64,18 @@ public class WxMiniAppPhoneCodeAuthenticationProvider implements AuthenticationP
         }
 
         String phoneNumber = phoneNumberInfo.getPhoneNumber();
-        log.info("通过code获取到手机号: {}", phoneNumber);
+        log.info("通过phoneCode获取到手机号: {}", phoneNumber);
 
-        // 2. 根据手机号注册或更新用户(c_user_info表)
-        UserInfo userInfo = userInfoService.registerOrUpdateUserByPhone(phoneNumber, null);
+        // 3. 根据手机号注册或更新用户,并保存openid(c_user_info表)
+        UserInfo userInfo = userInfoService.registerOrUpdateUserByPhone(phoneNumber, openId);
         
         if (userInfo == null) {
             throw new UsernameNotFoundException("用户注册失败");
         }
         
-        log.info("用户信息获取成功,ID: {}, 手机号: {}", userInfo.getId(), userInfo.getPhone());
+        log.info("用户信息获取成功,ID: {}, 手机号: {}, openid: {}", userInfo.getId(), userInfo.getPhone(), userInfo.getOpenid());
 
-        // 3. 构建小程序用户详情
+        // 4. 构建小程序用户详情
         AppletUserDetails userDetails = new AppletUserDetails();
         userDetails.setUserId(userInfo.getId());
         userDetails.setPhone(userInfo.getPhone());
@@ -70,7 +83,7 @@ public class WxMiniAppPhoneCodeAuthenticationProvider implements AuthenticationP
         userDetails.setNickname(userInfo.getNickName());
         userDetails.setEnabled(true);
 
-        // 4. 创建已认证的Token
+        // 5. 创建已认证的Token
         return WxMiniAppPhoneCodeAuthenticationToken.authenticated(
                 userDetails,
                 userDetails.getAuthorities()

+ 0 - 1
src/main/resources/mapper/business/UserInfoMapper.xml

@@ -32,7 +32,6 @@
                 ui.integral_num,
                 ui.group_id,
                 ua.balance,
-                ua.amount_owed,
                 uf.firm_id,
                 uf.type AS firmUserType,
                 fi.name AS firmName