Forráskód Böngészése

微信充值修改:请求充值时增加站点ID参数用于区分用户归属站点

skyline 1 éve
szülő
commit
152216386b

+ 3 - 3
car-wash-admin/src/main/java/com/kym/admin/controller/InvestorInfoController.java

@@ -6,7 +6,7 @@ import com.kym.common.utils.CommUtil;
 import com.kym.entity.InvestorInfo;
 import com.kym.entity.queryParams.CommonQueryParam;
 import com.kym.service.InvestorInfoService;
-import com.kym.service.cache.KymCache;
+import com.kym.service.cache.SysCache;
 import org.springframework.web.bind.annotation.*;
 
 /**
@@ -36,7 +36,7 @@ public class InvestorInfoController {
     @PostMapping("/create")
     R<?> create(@RequestBody InvestorInfo investorInfo) {
         investorInfo.setId(null);
-        investorInfo.setStationName(KymCache.INSTANCE.getStationNameById(investorInfo.getStationId()));
+        investorInfo.setStationName(SysCache.INSTANCE.getStationNameById(investorInfo.getStationId()));
         CommUtil.asserts(investorInfo.getAdminUserId() != null, "请选择关联客户");
         return R.success(investorInfoService.save(investorInfo));
     }
@@ -48,7 +48,7 @@ public class InvestorInfoController {
      */
     @PostMapping("/update")
     R<?> update(@RequestBody InvestorInfo investorInfo) {
-        investorInfo.setStationName(KymCache.INSTANCE.getStationNameById(investorInfo.getStationId()));
+        investorInfo.setStationName(SysCache.INSTANCE.getStationNameById(investorInfo.getStationId()));
         CommUtil.asserts(investorInfo.getAdminUserId() != null, "请选择关联客户");
         return R.success(investorInfoService.updateById(investorInfo));
     }

+ 33 - 0
car-wash-entity/src/main/java/com/kym/entity/MerchantAccount.java

@@ -0,0 +1,33 @@
+package com.kym.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.kym.entity.BaseEntity;
+import java.io.Serializable;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 商户账户表
+ * </p>
+ *
+ * @author skyline
+ * @since 2025-02-24
+ */
+@Getter
+@Setter
+@TableName("t_merchant_account")
+public class MerchantAccount extends BaseEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 总余额(分)
+     */
+    private Integer balance;
+
+    /**
+     * 冻结金额(分)
+     */
+    private Integer frozenAmount;
+}

+ 5 - 0
car-wash-entity/src/main/java/com/kym/entity/User.java

@@ -27,6 +27,11 @@ public class User extends BaseEntity implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
+    /**
+     * 归属站点id
+     */
+    private String stationId;
+
     /**
      * 微信openid
      */

+ 1 - 7
car-wash-entity/src/main/java/com/kym/entity/common/RedisKeys.java

@@ -6,22 +6,16 @@ package com.kym.entity.common;
  * @date 2023-07-31 18:26
  */
 public interface RedisKeys {
-    String EN_PLUS_TOKEN = "EN_PLUS_TOKEN";
-    String EN_PLUS_SASS_TOKEN = "EN_PLUS_SASS_TOKEN";
     String OFFLINE = "OFFLINE:";
     String OFFLINE_EXPIRED = "OFFLINE_EXPIRED:";
-    String CONNECTOR_ID_TO_SHORT_ID = "CONNECTOR_ID_TO_SHORT_ID:";
-    String CONNECTOR_ID_TO_STATUS = "CONNECTOR_ID_TO_STATUS:";
-    String CONNECTOR_ID_TO_STATION_ID = "CONNECTOR_ID_TO_STATION_ID:";
     String STATION_ID_TO_NAME = "STATION_ID_TO_NAME:";
-    String CONNECTOR_ID_TO_PARKING_NO = "CONNECTOR_ID_TO_PARKING_NO:";
     String ADMIN_USER_STATION_IDS = "ADMIN_USER_STATION_IDS:";
     String COUPON_ID_TO_USERS = "COUPON_ID_TO_USER_ID:";
-    String CHARGE_ORDER_EQUIP_CHARGE_STATUS = "CHARGE_ORDER_EQUIP_CHARGE_STATUS:";
 
     // =======================================洗车======================================
 
     String WASH_SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME = "WASH_SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME:";
+    String STATION_ID_TO_MERCHANT_ID = "STATION_ID_TO_MERCHANT_ID:";
 
     // =======================================洗车======================================
 

+ 1 - 0
car-wash-entity/src/main/java/com/kym/entity/vo/UserVo.java

@@ -18,6 +18,7 @@ import java.time.LocalDateTime;
 public class UserVo implements Serializable {
     private static final long serialVersionUID = 1L;
     public Long id;
+    private String stationId;
     public String username;
     public String nickname;
     public String openid;

+ 16 - 0
car-wash-mapper/src/main/java/com/kym/mapper/MerchantAccountMapper.java

@@ -0,0 +1,16 @@
+package com.kym.mapper;
+
+import com.kym.entity.MerchantAccount;
+import com.kym.mapper.mybatisplus.MyBaseMapper;
+
+/**
+ * <p>
+ * 商户账户表 Mapper 接口
+ * </p>
+ *
+ * @author skyline
+ * @since 2025-02-24
+ */
+public interface MerchantAccountMapper extends MyBaseMapper<MerchantAccount> {
+
+}

+ 16 - 0
car-wash-mapper/src/main/resources/mappers/MerchantAccountMapper.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.kym.mapper.MerchantAccountMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.kym.entity.MerchantAccount">
+        <result column="balance" property="balance" />
+        <result column="frozen_amount" property="frozenAmount" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        balance, frozen_amount
+    </sql>
+
+</mapper>

+ 4 - 4
car-wash-miniapp/src/main/java/com/kym/admin/controller/DailyStatController.java → car-wash-miniapp/src/main/java/com/kym/admin/controller/MerchantAccountController.java

@@ -5,14 +5,14 @@ import org.springframework.web.bind.annotation.RestController;
 
 /**
  * <p>
- * 日统计表 前端控制器
+ * 商户账户表 前端控制器
  * </p>
  *
  * @author skyline
- * @since 2025-02-19
+ * @since 2025-02-24
  */
 @RestController
-@RequestMapping("/daily-stat")
-public class DailyStatController {
+@RequestMapping("/merchant-account")
+public class MerchantAccountController {
 
 }

+ 3 - 10
car-wash-miniapp/src/main/java/com/kym/miniapp/controller/PaymentController.java

@@ -1,6 +1,5 @@
 package com.kym.miniapp.controller;
 
-import com.alibaba.fastjson2.JSONObject;
 import com.kym.common.R;
 import com.kym.common.annotation.ApiLog;
 import com.kym.service.wechat.WxPayService;
@@ -8,13 +7,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import lombok.SneakyThrows;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.*;
 
 
 /**
@@ -40,8 +33,8 @@ public class PaymentController {
     @ApiLog("微信支付")
     @GetMapping("/wxPay")
     @ResponseBody
-    R<?> prepay(@RequestParam Long rechargeConfigId) {
-        return R.success(wxPayService.wxPay(rechargeConfigId));
+    R<?> prepay(@RequestParam Long rechargeConfigId, @RequestParam String stationId) {
+        return R.success(wxPayService.wxPay(rechargeConfigId, stationId));
     }
 
     @ApiLog(value = "微信回调", ignoreParams = true)

+ 17 - 0
car-wash-service/src/main/java/com/kym/service/MerchantAccountService.java

@@ -0,0 +1,17 @@
+package com.kym.service;
+
+import com.kym.entity.MerchantAccount;
+import com.kym.service.mybatisplus.MyBaseService;
+
+/**
+ * <p>
+ * 商户账户表 服务类
+ * </p>
+ *
+ * @author skyline
+ * @since 2025-02-24
+ */
+public interface MerchantAccountService extends MyBaseService<MerchantAccount> {
+
+    MerchantAccount getStationBasicAccount(String stationId);
+}

+ 14 - 97
car-wash-service/src/main/java/com/kym/service/cache/KymCache.java

@@ -3,8 +3,8 @@ package com.kym.service.cache;
 import cn.hutool.extra.spring.SpringUtil;
 import com.kym.common.exception.BusinessException;
 import com.kym.common.utils.CommUtil;
-import com.kym.entity.common.RedisKeys;
 import com.kym.entity.WashStation;
+import com.kym.entity.common.RedisKeys;
 import com.kym.service.WashStationService;
 import org.springframework.boot.context.event.ApplicationStartedEvent;
 import org.springframework.context.ApplicationListener;
@@ -20,75 +20,42 @@ import java.util.stream.Collectors;
 /**
  * @author skyline
  * @description 缓存
- * @date 2023-08-25 22:58
  */
 public enum KymCache {
     INSTANCE;
 
-
-    private static ConcurrentHashMap<String, String> CONNECTOR_ID_SHORT_ID_MAPPING = new ConcurrentHashMap<>();
-    private static ConcurrentHashMap<String, String> SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME_MAPPING = new ConcurrentHashMap<>();
-
-
-    /**
-     * 获取枪头状态
-     *
-     * @param connectorId
-     * @return
-     */
-    public Integer getConnectorStatus(String connectorId) {
-        return Integer.valueOf(Objects.requireNonNull(KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.CONNECTOR_ID_TO_STATUS + connectorId)));
-    }
-
     /**
-     * 缓存枪头状态
-     *
-     * @param map
+     * 短编号与设备信息映射
      */
-    public void putConnectorId2Status(Map<String, Integer> map) {
-        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.CONNECTOR_ID_TO_STATUS + k, String.valueOf(v)));
-    }
+    private static ConcurrentHashMap<String, String> SHORT_ID_TO_PRODUCT_KEY_AND_DEVICE_NAME_MAPPING = new ConcurrentHashMap<>();
 
 
     /**
-     * 缓存枪头短编号
+     * 站点id与商户id映射
      *
      * @param map
      */
-    public void putConnectorId2ShortId(Map<String, String> map) {
-        CONNECTOR_ID_SHORT_ID_MAPPING.putAll(map);
-        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.CONNECTOR_ID_TO_SHORT_ID + k, v));
+    public static void putStationId2MerchantId(Map<String, String> map) {
+        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.STATION_ID_TO_MERCHANT_ID + k, v));
     }
 
     /**
-     * 通过设备编号或者枪头编号获取设备短编号
+     * 站点id与站点名称映射
      *
-     * @param id
-     * @return
+     * @param map
      */
-    public String getShortIdByEquipmentIdOrConnectorId(String id) {
-        var connectorId = getConnectorId(id);
-        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.CONNECTOR_ID_TO_SHORT_ID + connectorId);
-    }
-
-
-    public void putConnectorId2StationId(Map<String, String> map) {
-        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.CONNECTOR_ID_TO_STATION_ID + k, v));
+    public static void putStationId2Name(Map<String, String> map) {
+        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.STATION_ID_TO_NAME + k, v));
     }
 
     /**
-     * 通过设备编号或者枪头编号获取站点id
+     * 通过站点id获取商户id
      *
-     * @param id
+     * @param stationId
      * @return
      */
-    public String getStationIdByEquipmentIdOrConnectorId(String id) {
-        var connectorId = getConnectorId(id);
-        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.CONNECTOR_ID_TO_STATION_ID + connectorId);
-    }
-
-    public static void putStationId2Name(Map<String, String> map) {
-        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.STATION_ID_TO_NAME + k, v));
+    public String getMerchantIdByStationId(String stationId) {
+        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.STATION_ID_TO_MERCHANT_ID + stationId);
     }
 
     /**
@@ -101,16 +68,6 @@ public enum KymCache {
         return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.STATION_ID_TO_NAME + stationId);
     }
 
-    /**
-     * 通过充电接口id获取站点名称
-     *
-     * @param connectorId
-     * @return
-     */
-    public String getStationNameByConnectorId(String connectorId) {
-        var stationId = getStationIdByEquipmentIdOrConnectorId(connectorId);
-        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.STATION_ID_TO_NAME + stationId);
-    }
 
     /**
      * 操作员对应有权限的站点
@@ -126,46 +83,6 @@ public enum KymCache {
     }
 
 
-    /**
-     * 获取运营平台操作员有权限的站点ID
-     *
-     * @param adminUserId
-     * @return
-     */
-    public List<String> getAdminUserStationIds(Long adminUserId) {
-        return KymCacheInjector.redisTemplate.opsForSet().members(RedisKeys.ADMIN_USER_STATION_IDS + adminUserId).stream().toList();
-    }
-
-    public void putConnectorId2ParkingNo(Map<String, String> map) {
-        map.forEach((k, v) -> KymCacheInjector.redisTemplate.opsForValue().set(RedisKeys.CONNECTOR_ID_TO_PARKING_NO + k, v));
-    }
-
-    /**
-     * @param id
-     * @return
-     */
-    public String getParkNoByEquipmentIdOrConnectorId(String id) {
-        var connectorId = getConnectorId(id);
-        return KymCacheInjector.redisTemplate.opsForValue().get(RedisKeys.CONNECTOR_ID_TO_PARKING_NO + connectorId);
-    }
-
-    /**
-     * 将equipmentId,connectorId,shortId转化成17为的connectorId
-     *
-     * @param id
-     * @return
-     */
-    public String getConnectorId(String id) {
-        return switch (id.length()) {
-            case 17 -> id;
-            case 16 -> id.concat("1");
-            case 6 ->
-                    CONNECTOR_ID_SHORT_ID_MAPPING.entrySet().stream().filter(entry -> id.equals(entry.getValue())).map(Map.Entry::getKey).findFirst().get();
-            default -> null;
-        };
-    }
-
-
     /**
      * 缓存短编号和设备信息
      *

+ 25 - 0
car-wash-service/src/main/java/com/kym/service/impl/MerchantAccountServiceImpl.java

@@ -0,0 +1,25 @@
+package com.kym.service.impl;
+
+import com.kym.entity.MerchantAccount;
+import com.kym.mapper.MerchantAccountMapper;
+import com.kym.service.MerchantAccountService;
+import com.kym.service.cache.KymCache;
+import com.kym.service.mybatisplus.MyBaseServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 商户账户表 服务实现类
+ * </p>
+ *
+ * @author skyline
+ * @since 2025-02-24
+ */
+@Service
+public class MerchantAccountServiceImpl extends MyBaseServiceImpl<MerchantAccountMapper, MerchantAccount> implements MerchantAccountService {
+
+    @Override
+    public MerchantAccount getStationBasicAccount(String stationId) {
+        return getById(KymCache.INSTANCE.getMerchantIdByStationId(stationId));
+    }
+}

+ 3 - 1
car-wash-service/src/main/java/com/kym/service/wechat/WxPayService.java

@@ -5,6 +5,7 @@ import com.wechat.pay.java.service.refund.model.Refund;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.SneakyThrows;
 import org.springframework.http.ResponseEntity;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.io.IOException;
 import java.util.Map;
@@ -24,7 +25,8 @@ public interface WxPayService {
     @SneakyThrows
     ResponseEntity<Object> wxRefundNotify(HttpServletRequest request);
 
-    PrepayWithRequestPaymentResponse wxPay(Long rechargeConfigId);
+    @Transactional
+    PrepayWithRequestPaymentResponse wxPay(Long rechargeConfigId, String stationId);
 
     ResponseEntity<Object> wxNotify(HttpServletRequest request) throws IOException;
 

+ 18 - 5
car-wash-service/src/main/java/com/kym/service/wechat/impl/WxPayServiceImpl.java

@@ -15,8 +15,8 @@ import com.kym.common.utils.LambadaTools;
 import com.kym.common.utils.OrderUtils;
 import com.kym.entity.Account;
 import com.kym.entity.*;
-import com.kym.service.ActivityService;
 import com.kym.service.*;
+import com.kym.service.cache.KymCache;
 import com.kym.service.wechat.WxPayService;
 import com.wechat.pay.java.core.Config;
 import com.wechat.pay.java.core.RSAAutoCertificateConfig;
@@ -88,6 +88,7 @@ public class WxPayServiceImpl implements WxPayService {
     private final UserRechargeRightsService userRechargeRightsService;
 
     private final RechargeConfigService rechargeConfigService;
+    private final MerchantAccountService merchantAccountService;
 
 
     /**
@@ -100,7 +101,7 @@ public class WxPayServiceImpl implements WxPayService {
                             PayLogService payLogService, AccountService accountService,
                             RefundLogService refundLogService,
                             ActivityService activityService, UserRechargeRightsService userRechargeRightsService,
-                            RechargeConfigService rechargeConfigService) {
+                            RechargeConfigService rechargeConfigService, MerchantAccountService merchantAccountService) {
         this.conf = conf;
         this.walletDetailService = walletDetailService;
         this.payLogService = payLogService;
@@ -109,6 +110,7 @@ public class WxPayServiceImpl implements WxPayService {
         this.activityService = activityService;
         this.userRechargeRightsService = userRechargeRightsService;
         this.rechargeConfigService = rechargeConfigService;
+        this.merchantAccountService = merchantAccountService;
     }
 
     /**
@@ -207,7 +209,7 @@ public class WxPayServiceImpl implements WxPayService {
      */
     @Override
     @Transactional
-    public PrepayWithRequestPaymentResponse wxPay(Long rechargeConfigId) {
+    public PrepayWithRequestPaymentResponse wxPay(Long rechargeConfigId, String stationId) {
         // 充值配置
         var rechargeConfig = rechargeConfigService.getById(rechargeConfigId);
 
@@ -240,6 +242,8 @@ public class WxPayServiceImpl implements WxPayService {
         request.setDescription("超级进化车生活充值");
         request.setNotifyUrl(conf.getNotifyUrl());
         request.setOutTradeNo(outTradeNo);
+        // 把stationId传给微信,微信回调时携带再取出,记录是充值到哪个站点
+        request.setAttach(stationId);
         Payer payer = new Payer();
         payer.setOpenid(openid);
         request.setPayer(payer);
@@ -329,13 +333,22 @@ public class WxPayServiceImpl implements WxPayService {
                 payLog.setTradeType(transaction.getTradeType().name());
                 payLog.setTradeState(transaction.getTradeState().name());
                 payLog.setAttach(transaction.getAttach());
-                payLog.setTotal(transaction.getAmount().getTotal());
+                var totalAmount = transaction.getAmount().getTotal();
+                payLog.setTotal(totalAmount);
                 payLog.setCurrency(transaction.getAmount().getCurrency());
                 payLog.setPayerTotal(transaction.getAmount().getPayerTotal());
                 payLog.setPayerCurrency(transaction.getAmount().getPayerCurrency());
                 payLogService.save(payLog);
-                LOGGER.info("微信支付回调{}:业务处理结束", notifyRes[2]);
 
+                // 用户(此时要知道用户归属的站点)充值的资金先进到洗车站商户账户的基本户和冻结户,然后在消费时再将冻结户金额进行分润
+                var stationId = transaction.getAttach();
+                // 70%进入站点商户基本户,30%进入站点商户冻结户
+                var stationBasicAmount = (int) (totalAmount * 0.7);
+                var stationFreezeAmount = totalAmount - stationBasicAmount;
+                merchantAccountService.lambdaUpdate().setSql("balance = (balance + %d), frozen_amount = (frozen_amount + %d)".formatted(stationBasicAmount, stationFreezeAmount))
+                        .eq(MerchantAccount::getId, KymCache.INSTANCE.getMerchantIdByStationId(stationId)).update();
+
+                LOGGER.info("微信支付回调{}:业务处理结束", notifyRes[2]);
                 return ResponseEntity.status(HttpStatus.OK).build();
 
             } else {