liurunyu
2024-03-27 7914cc9dc7a5223b1804daa36a0614d00c92937f
Merge branch 'master' of http://8.140.179.55:20000/r/pipIrr-SV
35个文件已添加
4 文件已重命名
19个文件已删除
20个文件已修改
5697 ■■■■■ 已修改文件
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeClientCardMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeVcRechargeMapper.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeVcRefundItemMapper.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeVcRefundMapper.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeVirtualCardMapper.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVcRecharge.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVcRefund.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVcRefundItem.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVirtualCard.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voPr/VoController.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voPr/VoFlowMonitoring.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voSe/VoCards.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voSe/VoOrders.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/application-global.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrControllerMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrFlowMonitoringMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrIntakeControllerMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeClientCardMapper.xml 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVcRechargeMapper.xml 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVcRefundItemMapper.xml 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVcRefundMapper.xml 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/controller/ControllerCtrl.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/controller/ControllerSv.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/controller/QueryVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/pom.xml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/client/ClientCtrl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/clientCard/ClientCardCtrl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/clientCard/ClientCardSv.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/config/RestTemplateConfig.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/config/RestTemplateWechatCertConfig.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/result/SellResultCode.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/AesUtil.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/HmacSha256.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/PayHelper.java 506 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/RestTemplateUtil.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/VirtualCardCtrl.java 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/VirtualCardSv.java 274 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/dto/DtoAudit.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/dto/DtoRefund.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/dto/DtoVirtualCard.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/enums/LastOperateENUM.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/enums/OrderStateENUM.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/enums/RefundItemStateENUM.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/enums/RefundStateENUM.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wallet/WalletCtrl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wallet/WalletSv.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PayInfo.java 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PaymentCtrl.java 514 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PaymentSv.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/Code2Session.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/DtoOrder.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/OrderNotify.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/Orders.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/Refund.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/RefundRequest.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/RefundResponse.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/ToRefund.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/.gitignore 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/.mvn/wrapper/maven-wrapper.jar 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/.mvn/wrapper/maven-wrapper.properties 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/mvnw 308 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/mvnw.cmd 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/pom.xml 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/PipIrrWebChatApplication.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateConfig.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/WebFilterConfiguration.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/WebListenerConfiguration.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayHelper.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentCtrl.java 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentSv.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/result/WebChatResultCode.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/ICallback.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/OkHttpUtil.java 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/application.yml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/log4j2.yml 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/test/java/com/dy/pipirrWebChat/PipIrrWebWebchatApplicationTests.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pom.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeClientCardMapper.java
@@ -107,4 +107,16 @@
     * @return
     */
    List<VoCards> getCards(Map<?, ?> params);
    /**
     * 获取已挂失,未补卡的记录数量,应用程序使用
     * @return
     */
    Long getUnreplacedRecordCount();
    /**
     * 获取已挂失,未补卡的记录,应用程序使用
     * @return
     */
    List<VoCards> getUnreplaced(Map<?, ?> params);
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeVcRechargeMapper.java
New file
@@ -0,0 +1,52 @@
package com.dy.pipIrrGlobal.daoSe;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dy.pipIrrGlobal.pojoSe.SeVcRecharge;
import com.dy.pipIrrGlobal.voSe.VoOrders;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author ZhuBaoMin
 * @date 2024-03-05 20:21
 * @LastEditTime 2024-03-05 20:21
 * @Description
 */
@Mapper
public interface SeVcRechargeMapper extends BaseMapper<SeVcRecharge> {
    int deleteByPrimaryKey(Long id);
    int insert(SeVcRecharge record);
    int insertSelective(SeVcRecharge record);
    SeVcRecharge selectByPrimaryKey(Long id);
    int updateByPrimaryKeySelective(SeVcRecharge record);
    int updateByPrimaryKey(SeVcRecharge record);
    /**
     * 根据订单号获取虚拟卡充值对象
     * @param orderNumber
     * @return
     */
    SeVcRecharge getVCRechargeByorderNumber(String orderNumber);
    /**
     * 根据虚拟卡号获取订单列表
     * @param virtualId
     * @return
     */
    List<VoOrders> getOrders(@Param("virtualId") Long virtualId);
    /**
     * 根据订单号获取充值金额
     * @param orderNumber
     * @return
     */
    Integer getRechargeAmountByOrderNumber(@Param("orderNumber") String orderNumber);
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeVcRefundItemMapper.java
New file
@@ -0,0 +1,49 @@
package com.dy.pipIrrGlobal.daoSe;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 * @author ZhuBaoMin
 * @date 2024-03-10 21:05
 * @LastEditTime 2024-03-10 21:05
 * @Description
 */
@Mapper
public interface SeVcRefundItemMapper extends BaseMapper<SeVcRefundItem> {
    int deleteByPrimaryKey(Long id);
    int insert(SeVcRefundItem record);
    int insertSelective(SeVcRefundItem record);
    SeVcRefundItem selectByPrimaryKey(Long id);
    int updateByPrimaryKeySelective(SeVcRefundItem record);
    int updateByPrimaryKey(SeVcRefundItem record);
    /**
     * 根据订单号获取最后一个退单号
     * @param orderNumber
     * @return
     */
    String getLastRefundNumber(@Param("orderNumber") String orderNumber);
    /**
     * 根据退款通知接口返回的退款单号反查退款ID,查询该退款ID下未退款记录数量
     * @param refundNumber
     * @return
     */
    Integer getNoRefundedCount(String refundNumber);
    /**
     * 根据退款单号获取退款ID,退款通知后更新退款表所需
     * @param refundNumber
     * @return
     */
    Long getRefundIdByRefundNumber(String refundNumber);
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeVcRefundMapper.java
New file
@@ -0,0 +1,37 @@
package com.dy.pipIrrGlobal.daoSe;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefund;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author ZhuBaoMin
 * @date 2024-03-08 11:06
 * @LastEditTime 2024-03-08 11:06
 * @Description
 */
@Mapper
public interface SeVcRefundMapper extends BaseMapper<SeVcRefund> {
    int deleteByPrimaryKey(Long id);
    int insert(SeVcRefund record);
    int insertSelective(SeVcRefund record);
    SeVcRefund selectByPrimaryKey(Long id);
    int updateByPrimaryKeySelective(SeVcRefund record);
    int updateByPrimaryKey(SeVcRefund record);
    /**
     * 根据订单号获取其各笔退款金额
     * @param orderNumber
     * @return
     */
    List<Integer> getRefundAmount(@Param("orderNumber") String orderNumber);
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeVirtualCardMapper.java
New file
@@ -0,0 +1,28 @@
package com.dy.pipIrrGlobal.daoSe;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard;
import org.apache.ibatis.annotations.Mapper;
/**
 * @author ZhuBaoMin
 * @date 2024-03-05 20:45
 * @LastEditTime 2024-03-05 20:45
 * @Description
 */
@Mapper
public interface SeVirtualCardMapper extends BaseMapper<SeVirtualCard> {
    int deleteByPrimaryKey(Long id);
    int insert(SeVirtualCard record);
    int insertSelective(SeVirtualCard record);
    SeVirtualCard selectByPrimaryKey(Long id);
    int updateByPrimaryKeySelective(SeVirtualCard record);
    int updateByPrimaryKey(SeVirtualCard record);
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVcRecharge.java
New file
@@ -0,0 +1,102 @@
package com.dy.pipIrrGlobal.pojoSe;
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.writer.ObjectWriterImplToString;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dy.common.po.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import org.hibernate.validator.constraints.Length;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2024-03-05 20:21
 * @LastEditTime 2024-03-05 20:21
 * @Description
 */
@TableName(value="se_vc_recharge", autoResultMap = true)
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "虚拟卡充值实体")
public class SeVcRecharge implements BaseEntity {
    public static final long serialVersionUID = 202403052025001L;
    /**
    * 主键
    */
    @JSONField(serializeUsing= ObjectWriterImplToString.class)
    @TableId(type = IdType.INPUT)
    @Schema(description = "实体id", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Long id;
    /**
    * 虚拟卡ID
    */
    @Schema(description = "虚拟卡ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚拟卡ID不能为空")
    private Long vcId;
    /**
    * 农户ID
    */
    @Schema(description = "农户ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "农户ID不能为空")
    private Long clientId;
    /**
    * 钱包余额
    */
    @Schema(description = "钱包余额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "钱包余额不能为空")
    private Double money;
    /**
    * 充值后余额
    */
    @Schema(description = "充值后余额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "充值后余额不能为空")
    private Double afterRecharge;
    /**
    * 订单号
    */
    @Schema(description = "订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "订单号不能为空")
    private String orderNumber;
    /**
    * 充值金额
    */
    @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "充值金额不能为空")
    private Integer rechargeAmount;
    /**
    * 下单时间
    */
    @Schema(description = "下单时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Date orderTime;
    /**
    * 充值完成时间
    */
    @Schema(description = "充值完成时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Date rechargeTime;
    /**
    * 订单状态;1-未支付,2-已支付
    */
    @Schema(description = "订单状态", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @Length(message = "订单状态不大于{max},不小于{min}", min = 1, max = 2)
    private Byte orderState;
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVcRefund.java
New file
@@ -0,0 +1,116 @@
package com.dy.pipIrrGlobal.pojoSe;
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.writer.ObjectWriterImplToString;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dy.common.po.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2024-03-08 11:06
 * @LastEditTime 2024-03-08 11:06
 * @Description
 */
@TableName(value="se_vc_refund", autoResultMap = true)
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "虚拟卡退款实体")
public class SeVcRefund implements BaseEntity {
    public static final long serialVersionUID = 202403081109001L;
    /**
    * 主键
    */
    @JSONField(serializeUsing= ObjectWriterImplToString.class)
    @TableId(type = IdType.INPUT)
    @Schema(description = "实体id", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Long id;
    /**
    * 虚拟卡ID
    */
    @Schema(description = "虚拟卡ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚拟卡ID不能为空")
    private Long vcId;
    /**
    * 农户ID
    */
    @Schema(description = "农户ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "农户ID不能为空")
    private Long clientId;
    /**
    * 虚拟卡余额,退款通知后更新余额
    */
    @Schema(description = "虚拟卡余额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Double money;
    /**
    * 退款金额
    */
    @Schema(description = "退款金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "退款金额不能为空")
    private Integer refundAmount;
    /**
    * 退款后余额
    */
    @Schema(description = "退款后余额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Double afterRefund;
    /**
    * 申请时间
    */
    @Schema(description = "申请时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "申请时间不能为空")
    private Date applicationTime;
    /**
    * 审核人
    */
    @Schema(description = "审核人", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Long auditor;
    /**
    * 审核时间
    */
    @Schema(description = "审核时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Date auditTime;
    /**
    * 审核备注
    */
    @Schema(description = "审核备注", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private String remarks;
    /**
    * 退款单号;12位农户号+17位时间戳+2位数量
    */
    @Schema(description = "退款单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private String refundNumber;
    /**
    * 退款完成时间
    */
    @Schema(description = "退款完成时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Date refundTime;
    /**
    * 退款状态;1-待审核,2-待退款,3-已退款
    */
    @Schema(description = "退款状态", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Byte refundStatus;
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVcRefundItem.java
New file
@@ -0,0 +1,88 @@
package com.dy.pipIrrGlobal.pojoSe;
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.writer.ObjectWriterImplToString;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dy.common.po.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2024-03-10 21:05
 * @LastEditTime 2024-03-10 21:05
 * @Description
 */
@TableName(value="se_vc_refund_item", autoResultMap = true)
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "虚拟卡退款分项实体")
public class SeVcRefundItem implements BaseEntity {
    public static final long serialVersionUID = 202403102108001L;
    /**
     * 主键
     */
    @JSONField(serializeUsing= ObjectWriterImplToString.class)
    @TableId(type = IdType.INPUT)
    @Schema(description = "实体id", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Long id;
    /**
     * 退款ID
     */
    @Schema(description = "退款ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "退款ID不能为空")
    private Long refundId;
    /**
     * 订单号;12位农户好+17位时间戳
     */
    @Schema(description = "订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "订单号不能为空")
    private String orderNumber;
    /**
     * 退款单号;12位农户号+17位时间戳+2位数量
     */
    @Schema(description = "退款单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "退款单号不能为空")
    private String refundNumber;
    /**
     * 退款金额;与订单对应的退款金额
     */
    @Schema(description = "退款金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "退款金额不能为空")
    private Integer refundAmount;
    /**
     * 退款单创建时间
     */
    @Schema(description = "退款单创建时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "退款单创建时间不能为空")
    private Date createTime;
    /**
     * 退款完成时间
     */
    @Schema(description = "退款完成时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Date refundTime;
    /**
     * 退款状态;1-未退款,2-已退款
     */
    @Schema(description = "退款状态", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Byte refundStatus;
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVirtualCard.java
New file
@@ -0,0 +1,76 @@
package com.dy.pipIrrGlobal.pojoSe;
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.writer.ObjectWriterImplToString;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dy.common.po.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2024-03-05 20:45
 * @LastEditTime 2024-03-05 20:45
 * @Description
 */
@TableName(value="se_virtual_card", autoResultMap = true)
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "虚拟卡实体")
public class SeVirtualCard implements BaseEntity {
    public static final long serialVersionUID = 202403052048001L;
    /**
    * 主键
    */
    @JSONField(serializeUsing= ObjectWriterImplToString.class)
    @TableId(type = IdType.INPUT)
    @Schema(description = "实体id", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Long id;
    /**
    * 农户ID
    */
    @Schema(description = "农户ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "农户ID不能为空")
    private Long clientId;
    /**
    * 钱包余额
    */
    @Schema(description = "钱包余额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @Min(value = 0, message = "钱包余额不能小于0")
    private Double money;
    /**
    * 最后操作;1-开户,2-充值,3-消费,4-申请退款,5-退款审核
    */
    @Schema(description = "操作类型", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @Min(value = 1, message = "最后操作不能小于1")
    @Max(value = 5, message = "最后操作不能大于5")
    private Byte lastOperate;
    /**
    * 最后操作时间
    */
    @Schema(description = "最后操作时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Date lastOperateTime;
    /**
    * 创建时间
    */
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Date createTime;
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voPr/VoController.java
@@ -5,6 +5,8 @@
import com.dy.common.po.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
@@ -25,7 +27,7 @@
    @Schema(title = "ID")
    @ExcelProperty("ID")
    @ColumnWidth(10)
    private Long id;
    private String id;
    @Schema(title = "控制器地址")
    @ExcelProperty("控制器地址")
@@ -48,4 +50,9 @@
    @ExcelProperty("发现日期时间")
    @ColumnWidth(30)
    private Date findDt;
    @Schema(title = "在线状态")
    @ExcelProperty("在线状态")
    @ColumnWidth(6)
    private String onlineState;
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voPr/VoFlowMonitoring.java
@@ -21,7 +21,7 @@
    @Schema(title = "ID")
    @ExcelProperty("ID")
    @ColumnWidth(10)
    private Long id;
    private String id;
    @Schema(title = "管网流量监测站名称")
    @ExcelProperty("管网流量监测站名称")
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voSe/VoCards.java
@@ -26,13 +26,13 @@
    private String cardNum;
    @Schema(title = "电话号码")
    private Float phone;
    private String phone;
    @Schema(title = "身份证号码")
    private Float idCard;
    private String idCard;
    @Schema(title = "水卡状态")
    private Float cardState;
    private Integer cardState;
    @Schema(title = "水卡状态名称")
    private String stateName;
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voSe/VoOrders.java
New file
@@ -0,0 +1,44 @@
package com.dy.pipIrrGlobal.voSe;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2024-03-08 9:36
 * @LastEditTime 2024-03-08 9:36
 * @Description
 */
@Data
@Schema(title = "订单视图对象")
public class VoOrders {
    public static final long serialVersionUID = 202403072157001L;
    /**
     * 订单号
     */
    @Schema(description = "订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "订单号不能为空")
    private String orderNumber;
    /**
     * 充值金额
     */
    @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "充值金额不能为空")
    @Positive(message = "充值金额必须为大于0的整数")
    private Integer rechargeAmount;
    /**
     * 充值完成时间
     */
    @Schema(description = "充值完成时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "充值完成时间不能为空")
    private Date rechargeTime;
}
pipIrr-platform/pipIrr-global/src/main/resources/application-global.yml
@@ -112,10 +112,6 @@
        webPort: 8085
        actutorPort: 9085
        idSuffix: 7
    webchat:
        webPort: 8086
        actutorPort: 9086
        idSuffix: 8
#项目编号
#projectCode:
pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrControllerMapper.xml
@@ -30,7 +30,7 @@
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    <!--@mbg.generated-->
    select
    select
    <include refid="Base_Column_List" />
    from pr_controller
    where id = #{id,jdbcType=BIGINT}
@@ -172,7 +172,7 @@
        INNER JOIN pr_intake inta ON con.intakeId = inta.id
    <where>
      AND ic.operateType = 1
      AND deleted = 0
      AND con.deleted = 0
      <if test = "id != null and id > 0">
        AND con.id = ${id}
@@ -191,16 +191,18 @@
  <!--根据指定条件获取控制器记录-->
  <select id="getControllers" resultType="com.dy.pipIrrGlobal.voPr.VoController">
    SELECT
        CAST(con.id AS char) AS id,
        con.rtuAddr AS rtuAddr,
        inta.name AS intakeName,
        "在线" AS onlineState,
        (SELECT COUNT(*) FROM pr_intake_controller WHERE controllerId = con.id AND intakeId = inta.id AND operateType = 1) AS bindNumber,
        con.findDt
        con.findDt AS findDt
    FROM pr_controller con
        INNER JOIN pr_intake_controller ic ON ic.controllerId = con.id
        INNER JOIN pr_intake inta ON con.intakeId = inta.id
    <where>
      AND ic.operateType = 1
      AND deleted = 0
      AND con.deleted = 0
      <if test = "id != null and id > 0">
        AND con.id = ${id}
pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrFlowMonitoringMapper.xml
@@ -214,7 +214,7 @@
        INNER JOIN ba_district country ON pfm.countyId = country.id
        INNER JOIN ba_district town ON pfm.townId = town.id
        INNER JOIN ba_district village ON pfm.villageId = village.id
        INNER JOIN pr_monitoring_flowmeter pmofl ON pmofl.monitoringId = pfm.id
        LEFT JOIN pr_monitoring_flowmeter pmofl ON pmofl.monitoringId = pfm.id
        <where>
            pfm.deleted = 0
            <if test = "name != null and name !=''">
@@ -228,7 +228,7 @@
    <!--根据指定条件获取流量监测站实体记录-->
    <select id="getPrFlowMonitoring" parameterType="map" resultType="com.dy.pipIrrGlobal.voPr.VoFlowMonitoring">
        select (@i:=@i+1)                                           AS id,
        select  CAST(pfm.id AS char)                                 AS id,
                CONCAT(country.`name`, town.`name`, village.`name`) AS address,
                pfm.`name`                                          AS `name`,
                pmofl.operateType                                   AS bindState
@@ -236,8 +236,7 @@
        INNER JOIN ba_district country ON pfm.countyId = country.id
        INNER JOIN ba_district town ON pfm.townId = town.id
        INNER JOIN ba_district village ON pfm.villageId = village.id
        INNER JOIN pr_monitoring_flowmeter pmofl ON pmofl.monitoringId = pfm.id,
        (SELECT @i:=0) AS itable
        LEFT JOIN pr_monitoring_flowmeter pmofl ON pmofl.monitoringId = pfm.id
        <where>
            pfm.deleted = 0
            <if test = "name != null and name !=''">
pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrIntakeControllerMapper.xml
@@ -150,7 +150,8 @@
  <select id="getBindsByIntakeId" resultType="java.util.Map">
    SELECT
        user.name AS userName,
        con.code AS controllerCode,
        <!--con.code AS controllerCode,-->
        con.rtuAddr,
        (CASE
            WHEN ic.operateType = 1 THEN "绑定"
            WHEN ic.operateType = 2 THEN "解绑"
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeClientCardMapper.xml
@@ -364,7 +364,7 @@
            WHEN card.state = 3 THEN '已挂失'
        End) AS stateName,
        '农户卡' AS cardType,
        card.money
        FORMAT(card.money, 2) AS money
    FROM se_client_card card
        INNER JOIN se_client cli ON card.clientId = cli.id
    <where>
@@ -387,4 +387,40 @@
      </if>
    </trim>
  </select>
  <!--获取已挂失未补卡的记录数量-->
  <select id="getUnreplacedRecordCount" resultType="java.lang.Long">
    SELECT
        COUNT(*) AS recordCount
    FROM se_client_card card
        INNER JOIN se_client cli ON card.clientId = cli.id
    WHERE card.state = 3 AND NOT EXISTS (SELECT * FROM se_client_card card2 WHERE card.clientId = card2.clientId AND card2.state = 1)
  </select>
  <!--获取已挂失未补卡的记录-->
  <select id="getUnreplaced" resultType="com.dy.pipIrrGlobal.voSe.VoCards">
    SELECT
        cli.clientNum,
        cli.name AS clientName,
        card.cardNum,
        cli.phone,
        cli.idCard,
        card.state AS cardState,
        (CASE
            WHEN card.state = 1 THEN '正常'
            WHEN card.state = 2 THEN '已注销'
            WHEN card.state = 3 THEN '已挂失'
        End) AS stateName,
        '农户卡' AS cardType,
        FORMAT(card.money,2) AS money
    FROM se_client_card card
        INNER JOIN se_client cli ON card.clientId = cli.id
    WHERE card.state = 3 AND NOT EXISTS (SELECT * FROM se_client_card card2 WHERE card.clientId = card2.clientId AND card2.state = 1)
    ORDER BY card.id
    <trim prefix="limit " >
      <if test="start != null and count != null">
        #{start,javaType=Integer,jdbcType=INTEGER}, #{count,javaType=Integer,jdbcType=INTEGER}
      </if>
    </trim>
  </select>
</mapper>
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVcRechargeMapper.xml
New file
@@ -0,0 +1,199 @@
<?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.dy.pipIrrGlobal.daoSe.SeVcRechargeMapper">
  <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge">
    <!--@mbg.generated-->
    <!--@Table se_vc_recharge-->
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="vc_id" jdbcType="BIGINT" property="vcId" />
    <result column="client_id" jdbcType="BIGINT" property="clientId" />
    <result column="money" jdbcType="FLOAT" property="money" />
    <result column="after_recharge" jdbcType="FLOAT" property="afterRecharge" />
    <result column="order_number" jdbcType="VARCHAR" property="orderNumber" />
    <result column="recharge_amount" jdbcType="INTEGER" property="rechargeAmount" />
    <result column="order_time" jdbcType="TIMESTAMP" property="orderTime" />
    <result column="recharge_time" jdbcType="TIMESTAMP" property="rechargeTime" />
    <result column="order_state" jdbcType="TINYINT" property="orderState" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--@mbg.generated-->
    id, vc_id, client_id, money, after_recharge, order_number, recharge_amount, order_time,
    recharge_time, order_state
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    <!--@mbg.generated-->
    select
    <include refid="Base_Column_List" />
    from se_vc_recharge
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    <!--@mbg.generated-->
    delete from se_vc_recharge
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge">
    <!--@mbg.generated-->
    insert into se_vc_recharge (id, vc_id, client_id,
      money, after_recharge, order_number,
      recharge_amount, order_time, recharge_time,
      order_state)
    values (#{id,jdbcType=BIGINT}, #{vcId,jdbcType=BIGINT}, #{clientId,jdbcType=BIGINT},
      #{money,jdbcType=FLOAT}, #{afterRecharge,jdbcType=FLOAT}, #{orderNumber,jdbcType=VARCHAR},
      #{rechargeAmount,jdbcType=INTEGER}, #{orderTime,jdbcType=TIMESTAMP}, #{rechargeTime,jdbcType=TIMESTAMP},
      #{orderState,jdbcType=TINYINT})
  </insert>
  <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge">
    <!--@mbg.generated-->
    insert into se_vc_recharge
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="vcId != null">
        vc_id,
      </if>
      <if test="clientId != null">
        client_id,
      </if>
      <if test="money != null">
        money,
      </if>
      <if test="afterRecharge != null">
        after_recharge,
      </if>
      <if test="orderNumber != null">
        order_number,
      </if>
      <if test="rechargeAmount != null">
        recharge_amount,
      </if>
      <if test="orderTime != null">
        order_time,
      </if>
      <if test="rechargeTime != null">
        recharge_time,
      </if>
      <if test="orderState != null">
        order_state,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=BIGINT},
      </if>
      <if test="vcId != null">
        #{vcId,jdbcType=BIGINT},
      </if>
      <if test="clientId != null">
        #{clientId,jdbcType=BIGINT},
      </if>
      <if test="money != null">
        #{money,jdbcType=FLOAT},
      </if>
      <if test="afterRecharge != null">
        #{afterRecharge,jdbcType=FLOAT},
      </if>
      <if test="orderNumber != null">
        #{orderNumber,jdbcType=VARCHAR},
      </if>
      <if test="rechargeAmount != null">
        #{rechargeAmount,jdbcType=INTEGER},
      </if>
      <if test="orderTime != null">
        #{orderTime,jdbcType=TIMESTAMP},
      </if>
      <if test="rechargeTime != null">
        #{rechargeTime,jdbcType=TIMESTAMP},
      </if>
      <if test="orderState != null">
        #{orderState,jdbcType=TINYINT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge">
    <!--@mbg.generated-->
    update se_vc_recharge
    <set>
      <if test="vcId != null">
        vc_id = #{vcId,jdbcType=BIGINT},
      </if>
      <if test="clientId != null">
        client_id = #{clientId,jdbcType=BIGINT},
      </if>
      <if test="money != null">
        money = #{money,jdbcType=FLOAT},
      </if>
      <if test="afterRecharge != null">
        after_recharge = #{afterRecharge,jdbcType=FLOAT},
      </if>
      <if test="orderNumber != null">
        order_number = #{orderNumber,jdbcType=VARCHAR},
      </if>
      <if test="rechargeAmount != null">
        recharge_amount = #{rechargeAmount,jdbcType=INTEGER},
      </if>
      <if test="orderTime != null">
        order_time = #{orderTime,jdbcType=TIMESTAMP},
      </if>
      <if test="rechargeTime != null">
        recharge_time = #{rechargeTime,jdbcType=TIMESTAMP},
      </if>
      <if test="orderState != null">
        order_state = #{orderState,jdbcType=TINYINT},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge">
    <!--@mbg.generated-->
    update se_vc_recharge
    set vc_id = #{vcId,jdbcType=BIGINT},
      client_id = #{clientId,jdbcType=BIGINT},
      money = #{money,jdbcType=FLOAT},
      after_recharge = #{afterRecharge,jdbcType=FLOAT},
      order_number = #{orderNumber,jdbcType=VARCHAR},
      recharge_amount = #{rechargeAmount,jdbcType=INTEGER},
      order_time = #{orderTime,jdbcType=TIMESTAMP},
      recharge_time = #{rechargeTime,jdbcType=TIMESTAMP},
      order_state = #{orderState,jdbcType=TINYINT}
    where id = #{id,jdbcType=BIGINT}
  </update>
  <!--根据订单号获取虚拟卡充值对象-->
  <select id="getVCRechargeByorderNumber" resultMap="BaseResultMap">
    SELECT
        <include refid="Base_Column_List" />
    FROM se_vc_recharge
    WHERE order_number = #{orderNumber}
    LIMIT 0,1
  </select>
  <!--根据虚拟卡号获取订单列表-->
  <select id="getOrders" resultType="com.dy.pipIrrGlobal.voSe.VoOrders">
    SELECT
      order_number AS orderNumber,
      recharge_amount AS rechargeAmount,
      recharge_time AS rechargeTime
    FROM se_vc_recharge
    <where>
      AND order_state = 2
      <if test = "virtualId != null and virtualId > 0">
        AND vc_id = ${virtualId}
      </if>
    </where>
    ORDER BY order_number
  </select>
<!--根据订单号获取充值金额-->
  <select id="getRechargeAmountByOrderNumber" resultType="java.lang.Integer">
    SELECT
        recharge_amount AS rechargeAmount
    FROM se_vc_recharge
    <where>
      <if test = "orderNumber != null and orderNumber !=''">
        AND order_number = #{orderNumber}
      </if>
    </where>
  </select>
</mapper>
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVcRefundItemMapper.xml
New file
@@ -0,0 +1,168 @@
<?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.dy.pipIrrGlobal.daoSe.SeVcRefundItemMapper">
  <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem">
    <!--@mbg.generated-->
    <!--@Table se_vc_refund_item-->
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="refund_id" jdbcType="BIGINT" property="refundId" />
    <result column="order_number" jdbcType="VARCHAR" property="orderNumber" />
    <result column="refund_number" jdbcType="VARCHAR" property="refundNumber" />
    <result column="refund_amount" jdbcType="INTEGER" property="refundAmount" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    <result column="refund_time" jdbcType="TIMESTAMP" property="refundTime" />
    <result column="refund_status" jdbcType="TINYINT" property="refundStatus" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--@mbg.generated-->
    id, refund_id, order_number, refund_number, refund_amount, create_time, refund_time,
    refund_status
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    <!--@mbg.generated-->
    select
    <include refid="Base_Column_List" />
    from se_vc_refund_item
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    <!--@mbg.generated-->
    delete from se_vc_refund_item
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem">
    <!--@mbg.generated-->
    insert into se_vc_refund_item (id, refund_id, order_number,
      refund_number, refund_amount, create_time,
      refund_time, refund_status)
    values (#{id,jdbcType=BIGINT}, #{refundId,jdbcType=BIGINT}, #{orderNumber,jdbcType=VARCHAR},
      #{refundNumber,jdbcType=VARCHAR}, #{refundAmount,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP},
      #{refundTime,jdbcType=TIMESTAMP}, #{refundStatus,jdbcType=TINYINT})
  </insert>
  <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem">
    <!--@mbg.generated-->
    insert into se_vc_refund_item
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="refundId != null">
        refund_id,
      </if>
      <if test="orderNumber != null">
        order_number,
      </if>
      <if test="refundNumber != null">
        refund_number,
      </if>
      <if test="refundAmount != null">
        refund_amount,
      </if>
      <if test="createTime != null">
        create_time,
      </if>
      <if test="refundTime != null">
        refund_time,
      </if>
      <if test="refundStatus != null">
        refund_status,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=BIGINT},
      </if>
      <if test="refundId != null">
        #{refundId,jdbcType=BIGINT},
      </if>
      <if test="orderNumber != null">
        #{orderNumber,jdbcType=VARCHAR},
      </if>
      <if test="refundNumber != null">
        #{refundNumber,jdbcType=VARCHAR},
      </if>
      <if test="refundAmount != null">
        #{refundAmount,jdbcType=INTEGER},
      </if>
      <if test="createTime != null">
        #{createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="refundTime != null">
        #{refundTime,jdbcType=TIMESTAMP},
      </if>
      <if test="refundStatus != null">
        #{refundStatus,jdbcType=TINYINT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem">
    <!--@mbg.generated-->
    update se_vc_refund_item
    <set>
      <if test="refundId != null">
        refund_id = #{refundId,jdbcType=BIGINT},
      </if>
      <if test="orderNumber != null">
        order_number = #{orderNumber,jdbcType=VARCHAR},
      </if>
      <if test="refundNumber != null">
        refund_number = #{refundNumber,jdbcType=VARCHAR},
      </if>
      <if test="refundAmount != null">
        refund_amount = #{refundAmount,jdbcType=INTEGER},
      </if>
      <if test="createTime != null">
        create_time = #{createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="refundTime != null">
        refund_time = #{refundTime,jdbcType=TIMESTAMP},
      </if>
      <if test="refundStatus != null">
        refund_status = #{refundStatus,jdbcType=TINYINT},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem">
    <!--@mbg.generated-->
    update se_vc_refund_item
    set refund_id = #{refundId,jdbcType=BIGINT},
      order_number = #{orderNumber,jdbcType=VARCHAR},
      refund_number = #{refundNumber,jdbcType=VARCHAR},
      refund_amount = #{refundAmount,jdbcType=INTEGER},
      create_time = #{createTime,jdbcType=TIMESTAMP},
      refund_time = #{refundTime,jdbcType=TIMESTAMP},
      refund_status = #{refundStatus,jdbcType=TINYINT}
    where id = #{id,jdbcType=BIGINT}
  </update>
  <!--根据订单号获取最后一个退单号-->
  <select id="getLastRefundNumber" resultType="java.lang.String">
    SELECT
        refund_number AS refundNumber
    FROM se_vc_refund_item
    <where>
      <if test = "orderNumber != null">
        AND order_number = #{orderNumber}
      </if>
    </where>
    ORDER BY refund_number DESC
    LIMIT 0,1
  </select>
  <!--根据退款通知接口返回的退款单号反查退款ID,查询该退款ID下未退款记录数量-->
  <select id="getNoRefundedCount" resultType="java.lang.Integer">
    SELECT
      COUNT(*) AS recordCount
    FROM se_vc_refund_item
    WHERE refund_id = (SELECT refund_id FROM se_vc_refund_item WHERE refund_number = #{refundNumber}) AND refund_status = 1
  </select>
  <!--根据退款单号获取退款ID,退款通知后更新退款表所需-->
  <select id="getRefundIdByRefundNumber" resultType="java.lang.Long">
    SELECT
        refund_id AS refundId
    FROM se_vc_refund_item
    WHERE refund_number = #{refundNumber}
  </select>
</mapper>
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVcRefundMapper.xml
New file
@@ -0,0 +1,211 @@
<?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.dy.pipIrrGlobal.daoSe.SeVcRefundMapper">
  <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoSe.SeVcRefund">
    <!--@mbg.generated-->
    <!--@Table se_vc_refund-->
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="vc_id" jdbcType="BIGINT" property="vcId" />
    <result column="client_id" jdbcType="BIGINT" property="clientId" />
    <result column="money" jdbcType="FLOAT" property="money" />
    <result column="refund_amount" jdbcType="FLOAT" property="refundAmount" />
    <result column="after_refund" jdbcType="FLOAT" property="afterRefund" />
    <result column="application_time" jdbcType="TIMESTAMP" property="applicationTime" />
    <result column="auditor" jdbcType="BIGINT" property="auditor" />
    <result column="audit_time" jdbcType="TIMESTAMP" property="auditTime" />
    <result column="remarks" jdbcType="VARCHAR" property="remarks" />
    <result column="refund_number" jdbcType="VARCHAR" property="refundNumber" />
    <result column="refund_time" jdbcType="TIMESTAMP" property="refundTime" />
    <result column="refund_status" jdbcType="TINYINT" property="refundStatus" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--@mbg.generated-->
    id, vc_id, client_id, money, refund_amount, after_refund, application_time, auditor,
    audit_time, remarks, refund_number, refund_time, refund_status
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    <!--@mbg.generated-->
    select
    <include refid="Base_Column_List" />
    from se_vc_refund
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    <!--@mbg.generated-->
    delete from se_vc_refund
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRefund">
    <!--@mbg.generated-->
    insert into se_vc_refund (id, vc_id, client_id,
      money, refund_amount, after_refund,
      application_time, auditor, audit_time,
      remarks, refund_number, refund_time,
      refund_status)
    values (#{id,jdbcType=BIGINT}, #{vcId,jdbcType=BIGINT}, #{clientId,jdbcType=BIGINT},
      #{money,jdbcType=FLOAT}, #{refundAmount,jdbcType=FLOAT}, #{afterRefund,jdbcType=FLOAT},
      #{applicationTime,jdbcType=TIMESTAMP}, #{auditor,jdbcType=BIGINT}, #{auditTime,jdbcType=TIMESTAMP},
      #{remarks,jdbcType=VARCHAR}, #{refundNumber,jdbcType=VARCHAR}, #{refundTime,jdbcType=TIMESTAMP},
      #{refundStatus,jdbcType=TINYINT})
  </insert>
  <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRefund">
    <!--@mbg.generated-->
    insert into se_vc_refund
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="vcId != null">
        vc_id,
      </if>
      <if test="clientId != null">
        client_id,
      </if>
      <if test="money != null">
        money,
      </if>
      <if test="refundAmount != null">
        refund_amount,
      </if>
      <if test="afterRefund != null">
        after_refund,
      </if>
      <if test="applicationTime != null">
        application_time,
      </if>
      <if test="auditor != null">
        auditor,
      </if>
      <if test="auditTime != null">
        audit_time,
      </if>
      <if test="remarks != null">
        remarks,
      </if>
      <if test="refundNumber != null">
        refund_number,
      </if>
      <if test="refundTime != null">
        refund_time,
      </if>
      <if test="refundStatus != null">
        refund_status,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=BIGINT},
      </if>
      <if test="vcId != null">
        #{vcId,jdbcType=BIGINT},
      </if>
      <if test="clientId != null">
        #{clientId,jdbcType=BIGINT},
      </if>
      <if test="money != null">
        #{money,jdbcType=FLOAT},
      </if>
      <if test="refundAmount != null">
        #{refundAmount,jdbcType=FLOAT},
      </if>
      <if test="afterRefund != null">
        #{afterRefund,jdbcType=FLOAT},
      </if>
      <if test="applicationTime != null">
        #{applicationTime,jdbcType=TIMESTAMP},
      </if>
      <if test="auditor != null">
        #{auditor,jdbcType=BIGINT},
      </if>
      <if test="auditTime != null">
        #{auditTime,jdbcType=TIMESTAMP},
      </if>
      <if test="remarks != null">
        #{remarks,jdbcType=VARCHAR},
      </if>
      <if test="refundNumber != null">
        #{refundNumber,jdbcType=VARCHAR},
      </if>
      <if test="refundTime != null">
        #{refundTime,jdbcType=TIMESTAMP},
      </if>
      <if test="refundStatus != null">
        #{refundStatus,jdbcType=TINYINT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRefund">
    <!--@mbg.generated-->
    update se_vc_refund
    <set>
      <if test="vcId != null">
        vc_id = #{vcId,jdbcType=BIGINT},
      </if>
      <if test="clientId != null">
        client_id = #{clientId,jdbcType=BIGINT},
      </if>
      <if test="money != null">
        money = #{money,jdbcType=FLOAT},
      </if>
      <if test="refundAmount != null">
        refund_amount = #{refundAmount,jdbcType=FLOAT},
      </if>
      <if test="afterRefund != null">
        after_refund = #{afterRefund,jdbcType=FLOAT},
      </if>
      <if test="applicationTime != null">
        application_time = #{applicationTime,jdbcType=TIMESTAMP},
      </if>
      <if test="auditor != null">
        auditor = #{auditor,jdbcType=BIGINT},
      </if>
      <if test="auditTime != null">
        audit_time = #{auditTime,jdbcType=TIMESTAMP},
      </if>
      <if test="remarks != null">
        remarks = #{remarks,jdbcType=VARCHAR},
      </if>
      <if test="refundNumber != null">
        refund_number = #{refundNumber,jdbcType=VARCHAR},
      </if>
      <if test="refundTime != null">
        refund_time = #{refundTime,jdbcType=TIMESTAMP},
      </if>
      <if test="refundStatus != null">
        refund_status = #{refundStatus,jdbcType=TINYINT},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRefund">
    <!--@mbg.generated-->
    update se_vc_refund
    set vc_id = #{vcId,jdbcType=BIGINT},
      client_id = #{clientId,jdbcType=BIGINT},
      money = #{money,jdbcType=FLOAT},
      refund_amount = #{refundAmount,jdbcType=FLOAT},
      after_refund = #{afterRefund,jdbcType=FLOAT},
      application_time = #{applicationTime,jdbcType=TIMESTAMP},
      auditor = #{auditor,jdbcType=BIGINT},
      audit_time = #{auditTime,jdbcType=TIMESTAMP},
      remarks = #{remarks,jdbcType=VARCHAR},
      refund_number = #{refundNumber,jdbcType=VARCHAR},
      refund_time = #{refundTime,jdbcType=TIMESTAMP},
      refund_status = #{refundStatus,jdbcType=TINYINT}
    where id = #{id,jdbcType=BIGINT}
  </update>
  <!--根据订单号获取其各笔退款金额-->
  <select id="getRefundAmount" resultType="java.lang.Integer">
    SELECT
        refund_amount AS refundAmount
    FROM se_vc_refund
    <where>
      AND refund_status = 3
      <if test = "orderNumber != null">
        AND refund_amount LIKE CONCAT(#{orderNumber},'%')
      </if>
    </where>
  </select>
</mapper>
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml
New file
@@ -0,0 +1,116 @@
<?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.dy.pipIrrGlobal.daoSe.SeVirtualCardMapper">
  <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard">
    <!--@mbg.generated-->
    <!--@Table se_virtual_card-->
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="client_id" jdbcType="BIGINT" property="clientId" />
    <result column="money" jdbcType="FLOAT" property="money" />
    <result column="last_operate" jdbcType="TINYINT" property="lastOperate" />
    <result column="last_operate_time" jdbcType="TIMESTAMP" property="lastOperateTime" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--@mbg.generated-->
    id, client_id, money, last_operate, last_operate_time, create_time
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    <!--@mbg.generated-->
    select
    <include refid="Base_Column_List" />
    from se_virtual_card
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    <!--@mbg.generated-->
    delete from se_virtual_card
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard">
    <!--@mbg.generated-->
    insert into se_virtual_card (id, client_id, money,
      last_operate, last_operate_time, create_time
      )
    values (#{id,jdbcType=BIGINT}, #{clientId,jdbcType=BIGINT}, #{money,jdbcType=FLOAT},
      #{lastOperate,jdbcType=TINYINT}, #{lastOperateTime,jdbcType=TIMESTAMP}, #{createTime,jdbcType=TIMESTAMP}
      )
  </insert>
  <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard">
    <!--@mbg.generated-->
    insert into se_virtual_card
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="clientId != null">
        client_id,
      </if>
      <if test="money != null">
        money,
      </if>
      <if test="lastOperate != null">
        last_operate,
      </if>
      <if test="lastOperateTime != null">
        last_operate_time,
      </if>
      <if test="createTime != null">
        create_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=BIGINT},
      </if>
      <if test="clientId != null">
        #{clientId,jdbcType=BIGINT},
      </if>
      <if test="money != null">
        #{money,jdbcType=FLOAT},
      </if>
      <if test="lastOperate != null">
        #{lastOperate,jdbcType=TINYINT},
      </if>
      <if test="lastOperateTime != null">
        #{lastOperateTime,jdbcType=TIMESTAMP},
      </if>
      <if test="createTime != null">
        #{createTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard">
    <!--@mbg.generated-->
    update se_virtual_card
    <set>
      <if test="clientId != null">
        client_id = #{clientId,jdbcType=BIGINT},
      </if>
      <if test="money != null">
        money = #{money,jdbcType=FLOAT},
      </if>
      <if test="lastOperate != null">
        last_operate = #{lastOperate,jdbcType=TINYINT},
      </if>
      <if test="lastOperateTime != null">
        last_operate_time = #{lastOperateTime,jdbcType=TIMESTAMP},
      </if>
      <if test="createTime != null">
        create_time = #{createTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard">
    <!--@mbg.generated-->
    update se_virtual_card
    set client_id = #{clientId,jdbcType=BIGINT},
      money = #{money,jdbcType=FLOAT},
      last_operate = #{lastOperate,jdbcType=TINYINT},
      last_operate_time = #{lastOperateTime,jdbcType=TIMESTAMP},
      create_time = #{createTime,jdbcType=TIMESTAMP}
    where id = #{id,jdbcType=BIGINT}
  </update>
</mapper>
pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/controller/ControllerCtrl.java
@@ -44,13 +44,14 @@
@Slf4j
@Tag(name = "控制器管理", description = "控制器操作")
@RestController
@RequestMapping(path="controller")
@RequestMapping(path = "controller")
@RequiredArgsConstructor
public class ControllerCtrl {
    private final ControllerSv controllerSv;
    /**
     * 根据指定条件获取控制器列表
     *
     * @param vo 查询条件
     * @return 符合条件的控制器列表
     */
@@ -65,23 +66,24 @@
    })
    @GetMapping(path = "/getControllers")
    @SsoAop()
    public BaseResponse<QueryResultVo<List<VoController>>> getControllers(QueryVo vo){
    public BaseResponse<QueryResultVo<List<VoController>>> getControllers(QueryVo vo) {
        try {
            QueryResultVo<List<VoController>> res = controllerSv.getControllers(vo);
            if(res.itemTotal != null && res.itemTotal > 0) {
            if (res.itemTotal != null && res.itemTotal > 0) {
                return BaseResponseUtils.buildSuccess(res);
            }else {
            } else {
                return BaseResponseUtils.buildFail(ProjectResultCode.NO_RECORDS.getMessage());
            }
            //return BaseResponseUtils.buildSuccess(res);
        } catch (Exception e) {
            log.error("获取开卡记录异常", e);
            return BaseResponseUtils.buildException(e.getMessage()) ;
            return BaseResponseUtils.buildException(e.getMessage());
        }
    }
    /**
     * 根据控制器编号获取控制器列表
     *
     * @param rtuAddr 控制器地址
     * @return 符合条件的控制器列表
     */
@@ -96,22 +98,23 @@
    })
    @GetMapping(path = "controller_list")
    @SsoAop()
    public BaseResponse<List<Map<String, Object>>> getControllersByCode(String rtuAddr){
    public BaseResponse<List<Map<String, Object>>> getControllersByCode(String rtuAddr) {
        try {
            List<Map<String, Object>> list = Optional.ofNullable(controllerSv.getControllersByAddr(rtuAddr)).orElse(new ArrayList<>());
            if(list.size() <= 0) {
            if (list.size() <= 0) {
                return BaseResponseUtils.buildFail(ProjectResultCode.NO_RECORDS.getMessage());
            }
            return BaseResponseUtils.buildSuccess(list);
        } catch (Exception e) {
            log.error("查询控制器异常", e);
            return BaseResponseUtils.buildException(e.getMessage()) ;
            return BaseResponseUtils.buildException(e.getMessage());
        }
    }
    /**
     * 添加控制器
     * @param po 控制器实体对象
     *
     * @param po            控制器实体对象
     * @param bindingResult
     * @return 添加是否成功
     */
@@ -128,8 +131,8 @@
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    //public BaseResponse<Boolean> add(@RequestBody @Valid DtoController po, BindingResult bindingResult){
    public BaseResponse<Boolean> add(@RequestBody @Valid PrController po, BindingResult bindingResult){
        if(bindingResult != null && bindingResult.hasErrors()){
    public BaseResponse<Boolean> add(@RequestBody @Valid PrController po, BindingResult bindingResult) {
        if (bindingResult != null && bindingResult.hasErrors()) {
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
@@ -141,17 +144,21 @@
        Date operateTime = new Date();
        po.setOperateDt(operateTime);
        po.setDeleted((byte)0);
        po.setDeleted((byte) 0);
        po.setFindDt(operateTime);
        if (po.getAddWays() == null) {
            po.setAddWays((byte) 1);
        }
        Integer rec = Optional.ofNullable(controllerSv.addController(po)).orElse(0);
        if(rec == 0) {
        if (rec == 0) {
            return BaseResponseUtils.buildFail(ProjectResultCode.CONTROLLER_FAIL.getMessage());
        }
        return BaseResponseUtils.buildSuccess(true) ;
        return BaseResponseUtils.buildSuccess(true);
    }
    /**
     * 根据控制器编号删除控制器
     *
     * @param map
     * @return
     */
@@ -166,21 +173,22 @@
    })
    @PostMapping(path = "delete")
    @SsoAop()
    public BaseResponse<Boolean> delete(@RequestBody Map map){
        if(map == null || map.size() <=0) {
    public BaseResponse<Boolean> delete(@RequestBody Map map) {
        if (map == null || map.size() <= 0) {
            return BaseResponseUtils.buildFail(ProjectResultCode.PLEASE_INPUT_CONTROLLER_ID.getMessage());
        }
        Long controllerId = Long.parseLong(map.get("controllerId").toString());
        Integer recordCount = Optional.ofNullable(controllerSv.deleteControllerById(controllerId)).orElse(0);
        if(recordCount == 0) {
        if (recordCount == 0) {
            return BaseResponseUtils.buildFail(ProjectResultCode.DELETE_CONTROLLER_FAIL.getMessage());
        }
        return BaseResponseUtils.buildSuccess(true) ;
        return BaseResponseUtils.buildSuccess(true);
    }
    /**
     * 获取未绑控制器的取水口列表
     *
     * @return
     */
    @Operation(summary = "获得未绑控制器的取水口记录", description = "返回未绑控制器的取水口数据")
@@ -194,21 +202,22 @@
    })
    @GetMapping(path = "nobinding_intakes")
    @SsoAop()
    public BaseResponse<JSONArray> getNoBindingIntakes(){
    public BaseResponse<JSONArray> getNoBindingIntakes() {
        try {
            JSONArray array = controllerSv.getNoBindingIntakes();
            if(array.size() <= 0) {
            if (array.size() <= 0) {
                return BaseResponseUtils.buildFail(ProjectResultCode.NO_BINDING_INTAKE.getMessage());
            }
            return BaseResponseUtils.buildSuccess(array);
        } catch (Exception e) {
            log.error("查询控制器异常", e);
            return BaseResponseUtils.buildException(e.getMessage()) ;
            return BaseResponseUtils.buildException(e.getMessage());
        }
    }
    /**
     * 导出控制器列表
     *
     * @param response
     * @param vo
     */
pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/controller/ControllerSv.java
@@ -4,8 +4,10 @@
import com.alibaba.fastjson2.JSONArray;
import com.dy.common.webUtil.QueryResultVo;
import com.dy.pipIrrGlobal.daoPr.PrControllerMapper;
import com.dy.pipIrrGlobal.daoPr.PrIntakeControllerMapper;
import com.dy.pipIrrGlobal.daoPr.PrIntakeMapper;
import com.dy.pipIrrGlobal.pojoPr.PrController;
import com.dy.pipIrrGlobal.pojoPr.PrIntakeController;
import com.dy.pipIrrGlobal.voPr.VoController;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.PojoUtils;
@@ -33,6 +35,9 @@
    @Autowired
    private PrIntakeMapper prIntakeMapper;
    @Autowired
    private PrIntakeControllerMapper prIntakeControllerMapper;
    /**
     * 根据指定获取控制器记录
     * @param queryVo
@@ -58,7 +63,16 @@
     * @return
     */
    public Integer addController(PrController po) {
        return prControllerMapper.insert(po);
        prControllerMapper.insert(po);
        PrIntakeController addPrIntakeController = new PrIntakeController();
        addPrIntakeController.setIntakeid(po.getIntakeId());
        addPrIntakeController.setControllerid(po.getId());
        addPrIntakeController.setOperatetype((byte) 1);
        addPrIntakeController.setOperator(po.getOperator());
        addPrIntakeController.setOperatedt(po.getOperateDt());
        addPrIntakeController.setRemarks("绑定");
        prIntakeControllerMapper.insert(addPrIntakeController);
        return 1;
    }
    /**
pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/controller/QueryVo.java
@@ -26,6 +26,9 @@
    @Schema(description = "控制器编号")
    private String controllerCode;
    @Schema(description = "控制器地址")
    private String rtuAddr;
    @Schema(description = "在线状态")
    public Integer onlineState;
pipIrr-platform/pipIrr-web/pipIrr-web-sell/pom.xml
@@ -15,6 +15,15 @@
    <name>pipIrr-web-sell</name>
    <description>web营销信息系统</description>
    <dependencies>
        <!--OkHttp-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.2</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- 生成不包含依赖jar的可执行jar包
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/client/ClientCtrl.java
@@ -110,7 +110,7 @@
    @PostMapping(path = "add", consumes = MediaType.APPLICATION_JSON_VALUE)
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<Boolean> add(@RequestBody @Valid DtoClient po, BindingResult bindingResult){
    public BaseResponse<Boolean> add(@RequestBody @Valid DtoClient po, BindingResult bindingResult) {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/clientCard/ClientCardCtrl.java
@@ -146,4 +146,34 @@
            return BaseResponseUtils.buildException(e.getMessage()) ;
        }
    }
    /**
     * 获得一页已挂失,未补卡的水卡记录
     * @param vo
     * @return
     */
    @Operation(summary = "获得一页已挂失,未补卡的水卡记录", description = "返回一页已挂失,未补卡的水卡数据")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "返回一页农户数据(BaseResponse.content:QueryResultVo[{}])",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = BaClient.class))}
            )
    })
    @GetMapping(path = "getUnreplaced")
    @SsoAop()
    public BaseResponse<QueryResultVo<List<VoCards>>> getUnreplaced(QoCards vo){
        try {
            QueryResultVo<List<VoCards>> res = clientCardSv.getUnreplaced(vo);
            if(res.itemTotal != null && res.itemTotal > 0) {
                return BaseResponseUtils.buildSuccess(res);
            }else {
                return BaseResponseUtils.buildFail(SellResultCode.THE_CARD_NOT_EXIST.getMessage());
            }
        } catch (Exception e) {
            log.error("查询农户异常", e);
            return BaseResponseUtils.buildException(e.getMessage()) ;
        }
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/clientCard/ClientCardSv.java
@@ -130,4 +130,23 @@
        return rsVo ;
    }
    /**
     * 获取已挂失,未补卡的记录,应用程序使用
     * @param queryVo
     * @return
     */
    public QueryResultVo<List<VoCards>> getUnreplaced(QoCards queryVo){
        Map<String, Object> params = (Map<String, Object>) PojoUtils.generalize(queryVo) ;
        Long itemTotal = seClientCardMapper.getUnreplacedRecordCount();
        QueryResultVo<List<VoCards>> rsVo = new QueryResultVo<>() ;
        rsVo.pageSize = queryVo.pageSize ;
        rsVo.pageCurr = queryVo.pageCurr ;
        rsVo.calculateAndSet(itemTotal, params);
        rsVo.obj = seClientCardMapper.getUnreplaced(params);
        return rsVo ;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/config/RestTemplateConfig.java
New file
@@ -0,0 +1,45 @@
package com.dy.pipIrrSell.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 11:43
 * @LastEditTime 2024-03-06 11:43
 * @Description
 */
@Configuration
public class RestTemplateConfig {
    @Autowired
    private RestTemplateWechatCertConfig restTemplateWechatCertConfig;
    @Bean()
    public RestTemplate restTemplate() throws Exception {
        RestTemplate restTemplate = new RestTemplate(restTemplateWechatCertConfig.wechatHttpRequestFactory());
        // 添加拦截器
        //List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        //RestTemplateWechatCertConfig.MyRequestInterceptor myRequestInterceptor = new RestTemplateWechatCertConfig.MyRequestInterceptor();
        //interceptors.add(myRequestInterceptor);
        //restTemplate.setInterceptors(interceptors);
        // 中文乱码,主要是 StringHttpMessageConverter的默认编码为ISO导致的
        List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
        for (HttpMessageConverter converter : list) {
            if (converter instanceof StringHttpMessageConverter) {
                ((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8);
                break;
            }
        }
        return restTemplate;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/config/RestTemplateWechatCertConfig.java
File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateWechatCertConfig.java
@@ -1,6 +1,6 @@
package com.dy.pipirrWebChat.config;
package com.dy.pipIrrSell.config;
import com.dy.pipirrWebChat.payment.PayInfo;
import com.dy.pipIrrSell.wechatpay.PayInfo;
import okhttp3.OkHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
@@ -18,8 +18,8 @@
/**
 * @author ZhuBaoMin
 * @date 2024-02-23 19:18
 * @LastEditTime 2024-02-23 19:18
 * @date 2024-03-06 11:44
 * @LastEditTime 2024-03-06 11:44
 * @Description
 */
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/result/SellResultCode.java
@@ -79,18 +79,32 @@
    AUDITS_ADD_FAIL(80001, "总账审核记录添加失败"),
    /**
     * 电子钱包
     * 虚拟卡
     */
    VERIFY_FAIL(10001, "验签失败"),
    TOTAL_REFUND_EXCEED_TRADE(10001, "退款总额超过订单金额"),
    ACCEPTED(10001, "退款申请已受理"),
    PROCESSING(10001, "退款处理中"),
    ABNORMAL(10001, "退款异常"),
    RECHARGE_ADD_FAIL(10001, "充值记录添加失败"),
    CLIENT_ID_CANNOT_BE_NULL(90001, "农户编号不能为空"),
    WALLET_OPEN_ACCOUNT_FAIL(90002, "电子钱包账户注册失败"),
    WALLET_ACCOUNT_EXIST(90003, "该农户已注册电子钱包"),
    NO_ACCOUNT(90004, "您尚未注册电子钱包账户"),
    WALLET_OPEN_ACCOUNT_FAIL(90002, "虚拟卡账户注册失败"),
    //WALLET_ACCOUNT_EXIST(90003, "该农户已注册电子钱包"),
    NO_ACCOUNT(90004, "您指定的虚拟卡未注册"),
    UPDATE_ACCOUNT_FAIL(90005, "充值失败,电子钱包账户更新失败"),
    RECHARGE_FAIL(90006, "充值失败"),
    RECHARGE_NOT_EXIST(90006, "充值记录不存在"),
    VIRTUAL_CARD_NOT_EXIST(90006, "虚拟卡账户不存在"),
    BALANCE_IS_INSUFFICIENT(90007, "消费失败,余额不足"),
    CONSUME_FAIL(90008, "消费失败"),
    REFUND_AMOUNT_CANNOT_GREATER_THAN_MONEY(90009, "申请退款失败,退款金额不能大于余额"),
    APPLICATION_REFUND_FAIL(900010, "申请退款失败"),
    NOT_SUFFICIENT_FUNDS(900010, "退款失败,余额不足"),
    NO_TO_AUDIT_REFUND(900011, "该电子钱包没有待审核的退款申请"),
    AUDIT_REFUND_FAIL(900012, "审核退款申请失败"),
    No_WALLER_RECHARGES(900013, "没有符合条件的充值数据"),
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/AesUtil.java
New file
@@ -0,0 +1,51 @@
package com.dy.pipIrrSell.util;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 11:46
 * @LastEditTime 2024-03-06 11:46
 * @Description
 */
public class AesUtil {
    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;
    /**
     * 解密
     * @param apiV3Key apiV3密钥
     * @param associatedData 附加数据
     * @param nonce 随机串
     * @param ciphertext 数据密文
     * @return 解密后字符串
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static String decryptToString(byte[] apiV3Key, byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(apiV3Key, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/HmacSha256.java
New file
@@ -0,0 +1,43 @@
package com.dy.pipIrrSell.util;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 11:47
 * @LastEditTime 2024-03-06 11:47
 * @Description
 */
public class HmacSha256 {
    public static String getSignature(String secretKey, String data) throws NoSuchAlgorithmException, InvalidKeyException {
        // 创建密钥对象
        byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "HmacSHA256");
        // 创建Mac对象并初始化
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(secretKeySpec);
        // 将待加密的数据转换为字节数组
        byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
        // 使用密钥对数据进行加密
        byte[] encryptedBytes = mac.doFinal(dataBytes);
        // 将加密后的结果转换为十六进制字符串
        StringBuilder hexString = new StringBuilder();
        for (byte b : encryptedBytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        String hmacSha256 = hexString.toString();
        return hmacSha256;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/PayHelper.java
New file
@@ -0,0 +1,506 @@
package com.dy.pipIrrSell.util;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard;
import com.dy.pipIrrGlobal.voSe.VoOrders;
import com.dy.pipIrrSell.result.SellResultCode;
import com.dy.pipIrrSell.virtualCard.VirtualCardSv;
import com.dy.pipIrrSell.wechatpay.PayInfo;
import com.dy.pipIrrSell.wechatpay.dto.Refund;
import com.dy.pipIrrSell.wechatpay.dto.RefundRequest;
import com.dy.pipIrrSell.wechatpay.dto.RefundResponse;
import com.dy.pipIrrSell.wechatpay.dto.ToRefund;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 11:47
 * @LastEditTime 2024-03-06 11:47
 * @Description
 */
@Component
@RequiredArgsConstructor
public class PayHelper {
    private final VirtualCardSv virtualCardSv;
    private final RestTemplateUtil restTemplateUtil;
    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    private String checkSessionUrl = PayInfo.checkSessionUrl;
    private String tokenUrl = PayInfo.tokenUrl;
    private String resetUserSessionKeyUrl = PayInfo.resetUserSessionKeyUrl;
    private String notifyUrl = PayInfo.notifyUrl;
    private String schema = PayInfo.schema;
    private String privateCertFileName = PayInfo.privateCertFileName;
    private String refundUrl = PayInfo.refundUrl;
    // 平台证书公钥
    public Map<String, Certificate> CERTIFICATE_MAP = new HashMap();
    /**
     * 获取32位随机字符串
     * @return 随机串
     */
    public String generateRandomString() {
        Random random = new Random();
        StringBuilder sb = new StringBuilder(32);
        for (int i = 0; i < 32; i++) {
            int index = random.nextInt(CHARACTERS.length());
            sb.append(CHARACTERS.charAt(index));
        }
        return sb.toString();
    }
    /**
     * 获取商户证书私钥对象
     * @param filename 私钥文件路径
     * @return 私钥对象
     * @throws IOException
     */
    public PrivateKey getPrivateKey(String filename) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }
    /**
     * 检验登录态
     * @param appid 小程序 appId
     * @param secret 小程序 appSecret
     * @param openid 用户唯一标识符
     * @param sessionKey 会话密钥
     * @return
     * @throws IOException
     */
    public JSONObject checkSessionKey(String appid, String secret, String openid, String sessionKey) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
        String accessToken = "";
        Integer expiresIn = 0;
        String signature = HmacSha256.getSignature(sessionKey, "");
        String sigMethod = "hmac_sha256";
        JSONObject job_token = getAccessToken(appid, secret);
        if(job_token != null) {
            accessToken = job_token.getString("access_token");
            expiresIn = job_token.getInteger("expires_in");
        }
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("access_token", accessToken);
        queryParams.put("openid", openid);
        queryParams.put("signature", signature);
        queryParams.put("sig_method", sigMethod);
        Map<String, String> headerParams = new HashMap<>();
        JSONObject result = restTemplateUtil.get(checkSessionUrl, queryParams, headerParams);
        return result;
    }
    /**
     * 重置登录态
     * @param appid 小程序 appId
     * @param secret 小程序 appSecret
     * @param openid 用户唯一标识符
     * @param sessionKey 会话密钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws IOException
     */
    public JSONObject resetUserSessionKey(String appid, String secret, String openid, String sessionKey) throws NoSuchAlgorithmException, InvalidKeyException, IOException {
        String accessToken = "";
        Integer expiresIn = 0;
        String signature = HmacSha256.getSignature(sessionKey, "");
        String sigMethod = "hmac_sha256";
        JSONObject job_token = getAccessToken(appid, secret);
        if(job_token != null) {
            accessToken = job_token.getString("access_token");
            expiresIn = job_token.getInteger("expires_in");
        }
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("access_token", accessToken);
        queryParams.put("openid", openid);
        queryParams.put("signature", signature);
        queryParams.put("sig_method", sigMethod);
        Map<String, String> headerParams = new HashMap<>();
        JSONObject result = restTemplateUtil.get(resetUserSessionKeyUrl, queryParams, headerParams);
        return result;
    }
    /**
     * 获取接口调用凭据
     * @param appid 小程序 appId
     * @param secret 小程序 appSecret
     * @return 凭据及凭据有效时间
     * @throws IOException
     */
    public JSONObject getAccessToken(String appid, String secret) throws IOException {
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("grant_type", "client_credential");
        queryParams.put("appid", appid);
        queryParams.put("secret", secret);
        Map<String, String> headerParams = new HashMap<>();
        JSONObject job_result = restTemplateUtil.get(tokenUrl, queryParams, headerParams);
        return job_result;
    }
    /**
     * 构造签名串_下单
     * @param method HTTP请求方法
     * @param url URL
     * @param timestamp 时间戳
     * @param nonceStr 随机串
     * @param body 报文主题
     * @return 签名串
     */
    public String buildMessage_order(String method, String url, long timestamp, String nonceStr, String body) {
        return method + "\n"
                + url + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }
    /**
     * 构造签名串_再次下单
     * @param appid 小程序唯一标识
     * @param timestamp 时间戳
     * @param nonceStr 随机串
     * @param pkg package
     * @return 签名串
     */
    public String buildMessage_signAgain(String appid, String timestamp, String nonceStr, String pkg) {
        return appid + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + pkg + "\n";
    }
    /**
     * 签名
     * @param message 被签名信息
     * @param certFileName 私钥证书文件路径
     * @return signature签名值,签名信息中的一项,参与生成签名信息
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws SignatureException
     * @throws IOException
     */
    public String sign(byte[] message, String certFileName) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(certFileName));
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }
    /**
     * 获取签名信息
     * @param method
     * @param url
     * @param body
     * @return 签名信息,HTTP头中的签名信息
     * HTTP头:Authorization: 认证类型 签名信息
     * 认证类型,WECHATPAY2-SHA256-RSA2048
     */
    public String getToken(String method, String url, String body, String nonceStr, Long timestamp, String certFileName) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, NoSuchPaddingException {
        String message = buildMessage_order(method, url, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"), certFileName);
        return "mchid=\"" + PayInfo.mchid + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + PayInfo.serial_no + "\","
                + "signature=\"" + signature + "\"";
    }
    /**
     * 构造验造签名串
     * @param wechatpayTimestamp 请求头中返回的时间戳
     * @param wechatpayNonce 请求头中返回的随机串
     * @param boey 请求返回的body
     * @return signatureStr构造的验签名串
     */
    public String responseSign(String wechatpayTimestamp, String wechatpayNonce, String boey) {
        String signatureStr = wechatpayTimestamp + "\n"
                + wechatpayNonce + "\n"
                + boey + "\n";
        return signatureStr;
    }
    /**
     * 重新下载证书
     */
    public void refreshCertificate() throws GeneralSecurityException, IOException {
        String method = "GET";
        String httpUrl = "/v3/certificates";
        String nonceStr = generateRandomString();
        Long timestamp = System.currentTimeMillis() / 1000;
        String header = PayInfo.schema + " " + getToken(method, httpUrl, "", nonceStr, timestamp, PayInfo.privateCertFileName);
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", header);
        headers.put("Accept", "application/json");
        JSONObject job_result = restTemplateUtil.getHeaders(PayInfo.certificates,null, headers);
        JSONObject job_headers = job_result.getJSONObject("headers");
        String wechatpayNonce = job_headers.getJSONArray("Wechatpay-Nonce").getString(0);
        String wechatpaySerial = job_headers.getJSONArray("Wechatpay-Serial").getString(0);
        String signature_h = job_headers.getJSONArray("Wechatpay-Signature").getString(0);
        String signatureType_h = job_headers.getJSONArray("Wechatpay-Signature-Type").getString(0);
        String wechatpayTimestamp = job_headers.getJSONArray("Wechatpay-Timestamp").getString(0);
        JSONObject job_body = job_result.getJSONObject("body");
        if(job_body != null) {
            JSONArray array = job_body.getJSONArray("data");
            if(array != null && array.size() > 0) {
                for(int i = 0; i < array.size(); i++) {
                    JSONObject job_data = array.getJSONObject(i);
                    String certificateSerial = job_data.getString("serial_no");
                    String effective_time  = job_data.getString("effective_time");
                    String expire_time  = job_data.getString("expire_time");
                    JSONObject job_certificate = job_data.getJSONObject("encrypt_certificate");
                    String algorithm = job_certificate.getString("algorithm");
                    String nonce  = job_certificate.getString("nonce");
                    String associated_data  = job_certificate.getString("associated_data");
                    String ciphertext  = job_certificate.getString("ciphertext");
                    //对证书密文进行解密得到平台证书公钥
                    String publicKey = AesUtil.decryptToString(PayInfo.key.getBytes("utf-8"), associated_data.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
                    // 将平台公钥字符串转成Certificate对象
                    final CertificateFactory cf = CertificateFactory.getInstance("X509");
                    ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8));
                    Certificate certificate = null;
                    try {
                        certificate = cf.generateCertificate(inputStream);
                    } catch (CertificateException e) {
                        e.printStackTrace();
                    }
                    // 响应头证书序号与响应体证书序列号一致,且时间差小于5分钟时才将证书存储map
                    Long timeDiff = (System.currentTimeMillis() / 1000 - Long.parseLong(wechatpayTimestamp))/60;
                    if(wechatpaySerial.equals(certificateSerial) && timeDiff <= 5) {
                        // 证书放入MAP
                        CERTIFICATE_MAP.put(certificateSerial, certificate);
                    }
                }
            }
        }
    }
    /**
     * 使用微信平台证书进行响应验签
     * @param wechatpaySerial 来自响应头的微信平台证书序列号
     * @param signatureStr 构造的验签名串
     * @param wechatpaySignature 来自响应头的微信平台签名
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    public Boolean responseSignVerify(String wechatpaySerial, String signatureStr, String wechatpaySignature) throws GeneralSecurityException, IOException {
        if(CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) {
            CERTIFICATE_MAP.clear();
            refreshCertificate();
        }
        Certificate certificate = (Certificate)CERTIFICATE_MAP.get(wechatpaySerial);
        if(certificate == null) {
            return false;
        }
        // 获取公钥
        PublicKey publicKey = certificate.getPublicKey();
        // 初始化SHA256withRSA前面器
        Signature signature = Signature.getInstance("SHA256withRSA");
        // 用微信平台公钥对前面器进行初始化
        signature.initVerify(certificate);
        // 将构造的验签名串更新到签名器中
        signature.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        // 请求头中微信服务器返回的签名用Base64解码,使用签名器进行验证
        boolean valid = signature.verify(Base64.getDecoder().decode(wechatpaySignature));
        return valid;
    }
    /**
     * 获取待退款对象列表
     *      待退款对象包含订单号和可退款金额
     *      订单对象包含订单号、充值金额、充值完成时间
     *      1. 根据虚拟卡号到虚拟卡表中取出该卡余额
     *      2. 根据虚拟卡号到充值表取出订单对象列表
     * @param virtualId
     * @param refundAmount
     * @return
     */
    public List<ToRefund> getToRefunds(Long virtualId, Integer refundAmount) {
        ToRefund toRefund = new ToRefund();
        List<ToRefund> list = new ArrayList<>();
        Double money = 0d;
        // 根据虚拟卡号获取当前虚拟卡余额
        SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(virtualId);
        if(seVirtualCard != null) {
            money = seVirtualCard.getMoney();
        }
        // 要退金额大于该卡余额,返回空列表
        if(refundAmount > money) {
            return list;
        }
        // 根据虚拟卡号获取订单列表(仅限充值成功的)
        List<VoOrders> list_Orders = virtualCardSv.selectOrders(virtualId);
        // 遍历订单列表,获取
        if(list_Orders != null && list_Orders.size() > 0) {
            JSONArray array_Orders = (JSONArray) JSON.toJSON(list_Orders);
            for(int i = 0; i < array_Orders.size(); i++) {
                JSONObject job_order = array_Orders.getJSONObject(i);
                String orderNumber = job_order.getString("orderNumber");
                Integer rechargeAmount = job_order.getInteger("rechargeAmount");
                Date rechargeTime = job_order.getDate("rechargeTime");
                // 计算充值至今时间差(分钟)
                Long timestamp_Recharge = rechargeTime.getTime() / 1000;
                Long timestamp_Current = System.currentTimeMillis() / 1000;
                Long timeDiff_Minute = (timestamp_Current - timestamp_Recharge)/60;
                // 获取该订单已退款笔数
                Integer refundCount = 0;
                List<Integer> list_RefundAmount = virtualCardSv.selectRefundAmount(orderNumber);
                if(list_RefundAmount != null && list_RefundAmount.size() > 0) {
                    refundCount = list_RefundAmount.size();
                }
                // 充值至今未超过一年且该订单退款总次数未超过50次
                if(timeDiff_Minute/(365*24*60) >= 1 && (refundCount + 1) > 50)
                    return list;
                /**
                 * 1. 如果要退金额小于当前订单的充值金额,要退金额即为应退金额并返回
                 * 2. 如果要推金额大于当前订单充值金额,当前订单充值金额即为应退金额
                 *      a. 生成应退款对象
                 *      b. 计算新的余额
                 *      c. 金蒜新的要退款金额
                 *      d. 如果要退金额大于0,遍历下一个订单
                 */
                if(refundAmount <= rechargeAmount) {
                    toRefund = new ToRefund();
                    toRefund.setOrderNumber(orderNumber);
                    toRefund.setRefundAmount(refundAmount);
                    list.add(toRefund);
                    // 计算新的余额和新的要退金额
                    money = money - refundAmount;
                    refundAmount = refundAmount - refundAmount;
                    return list;
                }else {
                    toRefund = new ToRefund();
                    toRefund.setOrderNumber(orderNumber);
                    toRefund.setRefundAmount(rechargeAmount);
                    list.add(toRefund);
                    // 计算新的余额和新的要退金额
                    money = money - rechargeAmount;
                    refundAmount = refundAmount - rechargeAmount;
                    if(refundAmount > 0) {
                        continue;
                    }else {
                        return list;
                    }
                }
            }
        }
        return list;
    }
    /**
     * 退款申请,调用微信支付退款申请接口
     * @param po 退款请求对象,包含订单号、退款单号、退款金额
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws IOException
     * @throws SignatureException
     * @throws InvalidKeyException
     */
    public BaseResponse<Boolean> refunds(Refund po) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException {
        String tradeNo = po.getTradeNo();
        String refundNo = po.getRefundNo();
        Integer refund = po.getRefund();
        // 生成body
        Integer total = virtualCardSv.getRechargeAmountByOrderNumber(tradeNo);
        RefundRequest.Amount amount = new RefundRequest.Amount();
        amount.setRefund(refund);
        amount.setTotal(total);
        amount.setCurrency("CNY");
        RefundRequest refundRequest = new RefundRequest();
        refundRequest.setOut_trade_no(tradeNo);
        refundRequest.setOut_refund_no(refundNo);
        refundRequest.setNotify_url(notifyUrl);
        refundRequest.setAmount(amount);
        // 生成header
        String nonceStr = generateRandomString();
        Long timestamp = System.currentTimeMillis() / 1000;
        String method = "POST";
        String httpUrl = "/v3/refund/domestic/refunds";
        String body = JSONObject.toJSONString(refundRequest);
        String header = schema + " " + getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName);
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", header);
        headers.put("Accept", "application/json");
        headers.put("Content-Type", "application/json");
        JSONObject job_refundResponse = restTemplateUtil.post(PayInfo.refundUrl, body, headers);
        RefundResponse refundResponse = JSON.parseObject(job_refundResponse.toJSONString(), RefundResponse.class);
        String status = refundResponse.getStatus();
        if(status != null && status.equals("SUCCESS")) {
            // 退款申请已受理
            return BaseResponseUtils.buildSuccess(true) ;
        } else if(status != null && status.equals("PROCESSING")) {
            // 退款处理中
            return BaseResponseUtils.buildFail(SellResultCode.PROCESSING.getMessage());
        } else {
            // 退款异常
            return BaseResponseUtils.buildError(SellResultCode.ABNORMAL.getMessage());
        }
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/RestTemplateUtil.java
File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/RestTemplateUtil.java
@@ -1,4 +1,4 @@
package com.dy.pipirrWebChat.util;
package com.dy.pipIrrSell.util;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,8 +13,8 @@
/**
 * @author ZhuBaoMin
 * @date 2024-02-23 15:42
 * @LastEditTime 2024-02-23 15:42
 * @date 2024-03-06 13:47
 * @LastEditTime 2024-03-06 13:47
 * @Description
 */
@@ -39,6 +39,19 @@
        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(null, headers);
        ResponseEntity<String> response = restTemplate.exchange(tempUrl, HttpMethod.GET, httpEntity, String.class);
        return JSONObject.parseObject(response.getBody());
    }
    public JSONObject getHeaders(String url, Map<String, Object> queryParams, Map<String, String> headerParams) throws IOException {
        String tempUrl = setParamsByAppendUrl(queryParams, url);
        HttpHeaders headers = new HttpHeaders();
        headerParams.forEach(headers::add);
        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(null, headers);
        ResponseEntity<String> response = restTemplate.exchange(tempUrl, HttpMethod.GET, httpEntity, String.class);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("headers", response.getHeaders());
        jsonObject.put("body", response.getBody());
        return jsonObject;
    }
    public JSONObject get2(String url, Map<String, Object> queryParams, Map<String, String> headerParams) throws IOException {
@@ -99,4 +112,4 @@
        return url + sb;
    }
}
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/VirtualCardCtrl.java
New file
@@ -0,0 +1,228 @@
package com.dy.pipIrrSell.virtualCard;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.dy.common.aop.SsoAop;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.common.webUtil.ResultCodeMsg;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefund;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem;
import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard;
import com.dy.pipIrrSell.result.SellResultCode;
import com.dy.pipIrrSell.util.PayHelper;
import com.dy.pipIrrSell.virtualCard.dto.DtoAudit;
import com.dy.pipIrrSell.virtualCard.dto.DtoRefund;
import com.dy.pipIrrSell.virtualCard.enums.LastOperateENUM;
import com.dy.pipIrrSell.virtualCard.enums.RefundItemStateENUM;
import com.dy.pipIrrSell.virtualCard.enums.RefundStateENUM;
import com.dy.pipIrrSell.wechatpay.dto.Refund;
import com.dy.pipIrrSell.wechatpay.dto.ToRefund;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 8:40
 * @LastEditTime 2024-03-06 8:40
 * @Description
 */
@Slf4j
@Tag(name = "虚拟卡管理", description = "虚拟卡管理")
@RestController
@RequestMapping(path="virtual_card")
@RequiredArgsConstructor
@Validated
public class VirtualCardCtrl {
    private final VirtualCardSv virtualCardSv;
    private final PayHelper payHelper;
    /**
     * 虚拟卡账号注册
     * @param clientId
     * @return
     */
    @Operation(summary = "注册虚拟卡", description = "注册虚拟卡")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @PostMapping(path = "add_vc")
    @SsoAop()
    public BaseResponse<Boolean> addWallet(@RequestParam("clientId") @NotNull(message = "农户编号不能为空") Long clientId){
        if(clientId == null || clientId < 0) {
            return BaseResponseUtils.buildFail(SellResultCode.CLIENT_ID_CANNOT_BE_NULL.getMessage());
        }
        SeVirtualCard seVirtualCard = new SeVirtualCard();
        seVirtualCard.setClientId(clientId);
        seVirtualCard.setMoney(0d);
        seVirtualCard.setLastOperate(LastOperateENUM.OPEN_ACCOUNT.getCode());
        seVirtualCard.setLastOperateTime(new Date());
        seVirtualCard.setCreateTime(new Date());
        Long rec = virtualCardSv.insertVirtualCard(seVirtualCard);
        if(rec == null) {
            return BaseResponseUtils.buildFail(SellResultCode.WALLET_OPEN_ACCOUNT_FAIL.getMessage());
        }
        return BaseResponseUtils.buildSuccess(true) ;
    }
    /**
     * 用户申请退款
     * @param po
     * @param bindingResult
     * @return
     */
    @Operation(summary = "虚拟卡申请退款", description = "虚拟卡申请退款")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @PostMapping(path = "add_refund", consumes = MediaType.APPLICATION_JSON_VALUE)
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<Boolean> addRefund(@RequestBody @Valid DtoRefund po, BindingResult bindingResult){
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        Long virtualId = po.getVirtualId();
        Integer refundAmount = po.getRefundAmount();
        // 根据虚拟卡ID获取虚拟卡对象
        SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(virtualId);
        if(seVirtualCard == null) {
            return BaseResponseUtils.buildFail(SellResultCode.VIRTUAL_CARD_NOT_EXIST.getMessage());
        }
        Long clientId = seVirtualCard.getClientId();
        Double money = seVirtualCard.getMoney();
        // 验证退款金额是否大于余额
        if(refundAmount > money) {
            return BaseResponseUtils.buildFail(SellResultCode.REFUND_AMOUNT_CANNOT_GREATER_THAN_MONEY.getMessage());
        }
        // 计算消费后余额
        Double afterRefund = money - refundAmount;
        SeVcRefund seVcRefund = new SeVcRefund();
        seVcRefund.setVcId(virtualId);
        seVcRefund.setClientId(clientId);
        seVcRefund.setMoney(money);
        seVcRefund.setRefundAmount(refundAmount);
        seVcRefund.setAfterRefund(afterRefund);
        seVcRefund.setApplicationTime(new Date());
        seVcRefund.setRefundStatus(RefundStateENUM.TO_AUDIT.getCode());
        Long rec = virtualCardSv.addRefund(seVcRefund);
        if(rec == 0) {
            return BaseResponseUtils.buildFail(SellResultCode.APPLICATION_REFUND_FAIL.getMessage());
        }
        return BaseResponseUtils.buildSuccess(true) ;
    }
    /**
     * 审核退款申请
     * @param po
     * @param bindingResult
     * @return
     */
    @Operation(summary = "审核退款申请", description = "审核退款申请")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @PostMapping(path = "audit_refund", consumes = MediaType.APPLICATION_JSON_VALUE)
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<Boolean> auditRefund(@RequestBody @Valid DtoAudit po, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        // 根据退款ID获取退款对象,并更新审核人、审核时间、审核备注、退款状态字段
        SeVcRefund seVcRefund = virtualCardSv.selectRefundByRefundId(po.getRefundId());
        Long virtualId = seVcRefund.getVcId();
        Integer refundAmount = seVcRefund.getRefundAmount();
        seVcRefund.setAuditor(po.getAuditor());
        seVcRefund.setAuditTime(new Date());
        seVcRefund.setRemarks(po.getRemarks());
        seVcRefund.setRefundStatus(RefundStateENUM.TO_REFUND.getCode());
        Integer rec = virtualCardSv.updateRefund(seVcRefund);
        if(rec == 0) {
            return BaseResponseUtils.buildFail(SellResultCode.AUDIT_REFUND_FAIL.getMessage());
        }
        // 完成审核后获取待退款订单列表
        List<ToRefund> list_ToRefund = payHelper.getToRefunds(virtualId, refundAmount);
        if(list_ToRefund == null || list_ToRefund.size() <=0)
            return BaseResponseUtils.buildFail(SellResultCode.NOT_SUFFICIENT_FUNDS.getMessage());
        //遍历待退款列表
        JSONArray array_ToRefund = (JSONArray) JSON.toJSON(list_ToRefund);
        for(int i = 0; i < array_ToRefund.size(); i++) {
            JSONObject job_ToRefund = array_ToRefund.getJSONObject(i);
            String orderNumber_ToRefund = job_ToRefund.getString("orderNumber");
            Integer refundAmount_ToRefund = job_ToRefund.getInteger("refundAmount");
            // 生成退款分项记录
            SeVcRefundItem seVcRefundItem = new SeVcRefundItem();
            seVcRefundItem.setRefundId(po.getRefundId());
            seVcRefundItem.setOrderNumber(orderNumber_ToRefund);
            String refundNumber = virtualCardSv.generateRefundNumber(orderNumber_ToRefund);
            seVcRefundItem.setRefundNumber(refundNumber);
            seVcRefundItem.setRefundAmount(refundAmount_ToRefund);
            seVcRefundItem.setCreateTime(new Date());
            seVcRefundItem.setRefundStatus(RefundItemStateENUM.NO_REFUND.getCode());
            Long refundItemId = virtualCardSv.addRefundItem(seVcRefundItem);
            // 调用微信退款申请接口
            Refund refund = new Refund();
            refund.setTradeNo(orderNumber_ToRefund);
            refund.setRefundNo(refundNumber);
            refund.setRefund(refundAmount_ToRefund);
            BaseResponse rep = payHelper.refunds(refund);
        }
        return BaseResponseUtils.buildSuccess(true) ;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/VirtualCardSv.java
New file
@@ -0,0 +1,274 @@
package com.dy.pipIrrSell.virtualCard;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.pipIrrGlobal.daoSe.SeVcRechargeMapper;
import com.dy.pipIrrGlobal.daoSe.SeVcRefundItemMapper;
import com.dy.pipIrrGlobal.daoSe.SeVcRefundMapper;
import com.dy.pipIrrGlobal.daoSe.SeVirtualCardMapper;
import com.dy.pipIrrGlobal.pojoSe.SeVcRecharge;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefund;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem;
import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard;
import com.dy.pipIrrGlobal.voSe.VoOrders;
import com.dy.pipIrrSell.result.SellResultCode;
import com.dy.pipIrrSell.virtualCard.dto.DtoVirtualCard;
import com.dy.pipIrrSell.virtualCard.enums.LastOperateENUM;
import com.dy.pipIrrSell.virtualCard.enums.OrderStateENUM;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 8:41
 * @LastEditTime 2024-03-06 8:41
 * @Description
 */
@Slf4j
@Service
public class VirtualCardSv {
    @Autowired
    private SeVirtualCardMapper seVirtualCardMapper;
    @Autowired
    private SeVcRechargeMapper seVcRechargeMapper;
    @Autowired
    private SeVcRefundMapper seVcRefundMapper;
    @Autowired
    private SeVcRefundItemMapper seVcRefundItemMapper;
    /**
     * 注册虚拟卡
     * @param po
     * @return
     */
    public Long insertVirtualCard(SeVirtualCard po) {
        seVirtualCardMapper.insert(po);
        return po.getId();
    }
    /**
     * 修改虚拟卡
     * 充值、消费、申请退款、审核退款时需要修改虚拟卡的:余额、最后操作、最后操作时间
     * @param po
     * @return
     */
    public Integer updateVirtualCard(SeVirtualCard po) {
        return seVirtualCardMapper.updateByPrimaryKeySelective(po);
    }
    /**
     * 根据虚拟卡编号获取虚拟卡对象
     * @param virtualId
     * @return
     */
    public SeVirtualCard selectVirtuCardById(Long virtualId) {
        return seVirtualCardMapper.selectByPrimaryKey(virtualId);
    }
    /**
     * 添加虚拟卡充值记录
     * JSAPI下单后生成部分充值记录
     * @param po
     * @return
     */
    public BaseResponse<Boolean> insertVCRecharge(DtoVirtualCard po) {
        String orderNumber = po.getOrderNumber();
        Long virtualId = po.getVirtualId();
        Long clientId = po.getClientId();
        Integer rechargeAmount = po.getRechargeAmount();
        // 验证该虚拟卡账户是否存在并取出当前账户余额
        SeVirtualCard seVirtualCard = seVirtualCardMapper.selectByPrimaryKey(virtualId);
        if(seVirtualCard == null) {
            return BaseResponseUtils.buildFail(SellResultCode.NO_ACCOUNT.getMessage());
        }
        Double money = seVirtualCard.getMoney();
        // 添加充值记录
        SeVcRecharge seVcRecharge = new SeVcRecharge();
        seVcRecharge.setVcId(virtualId);
        seVcRecharge.setClientId(clientId);
        seVcRecharge.setMoney(money);
        seVcRecharge.setOrderNumber(orderNumber);
        seVcRecharge.setRechargeAmount(rechargeAmount);
        seVcRecharge.setOrderTime(new Date());
        seVcRecharge.setOrderState(OrderStateENUM.NON_PAYMENT.getCode());
        Integer rec = seVcRechargeMapper.insert(seVcRecharge);
        if(rec == null) {
            return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_FAIL.getMessage());
        }
        return BaseResponseUtils.buildSuccess(true) ;
    }
    /**
     * 修改虚拟卡充值记录
     * 微信支付通知后:
     *      1. 更新充值表:充值后余额、支付完成时间、订单状态
     *      2. 更新虚拟卡表:账户余额、最后操作、最后操作时间
     * @param orderNumber 订单编号
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<Boolean> updateVCRecharge(String orderNumber, Date rechargeTime) {
        SeVcRecharge seVcRecharge = seVcRechargeMapper.getVCRechargeByorderNumber(orderNumber);
        if(seVcRecharge == null) {
            return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_NOT_EXIST.getMessage());
        }
        Long virtualId = seVcRecharge.getVcId();
        Double money = seVcRecharge.getMoney();
        Integer rechargeAmount = seVcRecharge.getRechargeAmount();
        Double afterRrecharge = money + rechargeAmount;
        seVcRecharge.setAfterRecharge(afterRrecharge);
        seVcRecharge.setRechargeTime(rechargeTime);
        seVcRecharge.setOrderState(OrderStateENUM.PAID.getCode());
        Integer rec = seVcRechargeMapper.updateByPrimaryKeySelective(seVcRecharge);
        if(rec == null) {
            return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_FAIL.getMessage());
        }
        SeVirtualCard seVirtualCard = seVirtualCardMapper.selectByPrimaryKey(virtualId);
        if(seVirtualCard == null) {
            return BaseResponseUtils.buildFail(SellResultCode.VIRTUAL_CARD_NOT_EXIST.getMessage());
        }
        seVirtualCard.setMoney(afterRrecharge);
        seVirtualCard.setLastOperate(LastOperateENUM.RECHARGE.getCode());
        seVirtualCard.setLastOperateTime(new Date());
        Integer rec2 = seVirtualCardMapper.updateByPrimaryKeySelective(seVirtualCard);
        if(rec2 == null) {
            return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_FAIL.getMessage());
        }
        return BaseResponseUtils.buildSuccess(true) ;
    }
    /**
     * 修改虚拟卡充值记录(废弃)
     * 微信小程序支付通知后修改:余额、充值后余额、充值完成时间
     * @param po
     * @return
     */
    public Integer updateVCRecharge(SeVcRecharge po) {
        return seVcRechargeMapper.updateByPrimaryKeySelective(po);
    }
    /**
     * 根据虚拟卡号获取订单列表
     * @param virtualId
     * @return
     */
    public List<VoOrders> selectOrders(Long virtualId) {
        List<VoOrders> rsVo = seVcRechargeMapper.getOrders(virtualId);
        return rsVo ;
    }
    /**
     * 根据退款ID获取退款对象
     * @param refundId
     * @return
     */
    public SeVcRefund selectRefundByRefundId(Long refundId) {
        return seVcRefundMapper.selectByPrimaryKey(refundId);
    }
    /**
     * 添加退款申请
     * @param po
     * @return
     */
    public Long addRefund(SeVcRefund po) {
        seVcRefundMapper.insert(po);
        return po.getId();
    }
    /**
     * 修改退款记录
     * @param po
     * @return
     */
    public Integer updateRefund(SeVcRefund po) {
        return seVcRefundMapper.updateByPrimaryKeySelective(po);
    }
    /**
     * 根据订单号获取其各笔退款金额
     * @param orderNumber
     * @return
     */
    public List<Integer> selectRefundAmount(String orderNumber) {
        List<Integer> rsVo = seVcRefundMapper.getRefundAmount(orderNumber);
        return rsVo;
    }
    /**
     * 添加退款分项
     * @param po
     * @return
     */
    public Long addRefundItem(SeVcRefundItem po) {
        seVcRefundItemMapper.insert(po);
        return po.getRefundId();
    }
    /**
     * 编辑退款分项
     * @param po
     * @return
     */
    public Integer updateRefundItem(SeVcRefundItem po) {
        return seVcRefundItemMapper.updateByPrimaryKeySelective(po);
    }
    /**
     * 根据订单号生成退款单号
     * @param orderNumber
     * @return
     */
    public String generateRefundNumber(String orderNumber) {
        String refundNumber = seVcRefundItemMapper.getLastRefundNumber(orderNumber);
        if(refundNumber == null) {
            refundNumber = orderNumber + "01";
            return refundNumber;
        }
        String a = String.format("%02d", (Integer.parseInt(refundNumber.substring(29,30).trim()) + 1));
        return  a;
    }
    /**
     * 根据订单号获取充值金额,调用退款申请接口使用
     * @param orderNumber
     * @return
     */
    public Integer getRechargeAmountByOrderNumber(String orderNumber) {
        return seVcRechargeMapper.getRechargeAmountByOrderNumber(orderNumber);
    }
    /**
     * 根据退款通知接口返回的退款单号反查退款ID,查询该退款ID下未退款记录数量
     * @param refundNumber
     * @return
     */
    public Integer getNoRefundedCount(String refundNumber) {
        return seVcRefundItemMapper.getNoRefundedCount(refundNumber);
    }
    /**
     * 根据退款单号获取退款ID,退款通知后更新退款表所需
     * @param refundNumber
     * @return
     */
    public Long getRefundIdByRefundNumber(String refundNumber) {
        return seVcRefundItemMapper.getRefundIdByRefundNumber(refundNumber);
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/dto/DtoAudit.java
New file
@@ -0,0 +1,40 @@
package com.dy.pipIrrSell.virtualCard.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
/**
 * @author ZhuBaoMin
 * @date 2024-03-08 21:09
 * @LastEditTime 2024-03-08 21:09
 * @Description
 */
@Data
@Schema(name = "退款审核传入对象")
public class DtoAudit {
    public static final long serialVersionUID = 202403082109001L;
    /**
     * 虚拟卡退款ID
     */
    @Schema(description = "虚拟卡退款ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚拟卡退款ID不能为空")
    private Long refundId;
    /**
     * 审核人ID
     */
    @Schema(description = "审核人ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "审核ID不能为空")
    private Long auditor;
    /**
     * 审核备注
     */
    @Schema(description = "审核备注", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @Length(max = 200)
    private String remarks;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/dto/DtoRefund.java
New file
@@ -0,0 +1,34 @@
package com.dy.pipIrrSell.virtualCard.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-03-08 20:17
 * @LastEditTime 2024-03-08 20:17
 * @Description
 */
@Data
@Schema(name = "用户退款传入对象")
public class DtoRefund {
    public static final long serialVersionUID = 202403080858001L;
    /**
     * 虚拟卡编号
     */
    @Schema(description = "虚拟卡编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚拟卡编号不能为空")
    private Long virtualId;
    /**
     * 退款金额
     */
    @Schema(description = "退款金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "退款金额不能为空")
    @Positive(message = "充值金额必须为大于0的整数")
    private Integer refundAmount;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/dto/DtoVirtualCard.java
New file
@@ -0,0 +1,47 @@
package com.dy.pipIrrSell.virtualCard.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 9:40
 * @LastEditTime 2024-03-06 9:40
 * @Description
 */
@Data
@Schema(name = "虚拟卡充值传入对象")
public class DtoVirtualCard {
    public static final long serialVersionUID = 202403060943001L;
    /**
     * 订单号
     */
    @Schema(description = "订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "订单号不能为空")
    private String orderNumber;
    /**
     * 农户ID
     */
    @Schema(description = "农户ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "农户ID不能为空")
    private Long clientId;
    /**
     * 虚拟卡ID
     */
    @Schema(description = "虚拟卡ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚拟卡ID不能为空")
    private Long virtualId;
    /**
     * 充值金额
     */
    @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "充值金额不能为空")
    private Integer rechargeAmount;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/enums/LastOperateENUM.java
New file
@@ -0,0 +1,25 @@
package com.dy.pipIrrSell.virtualCard.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 9:21
 * @LastEditTime 2024-03-06 9:21
 * @Description
 */
@Getter
@AllArgsConstructor
public enum LastOperateENUM {
    OPEN_ACCOUNT((byte)1, "开户"),
    RECHARGE((byte)2, "充值"),
    CONSUME((byte)3, "消费"),
    APPLY_REFUND((byte)4, "申请退款"),
    AUDIT_REFUND((byte)5, "退款审核"),
    REFUND((byte)6, "退款");
    private final Byte code;
    private final String message;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/enums/OrderStateENUM.java
New file
@@ -0,0 +1,21 @@
package com.dy.pipIrrSell.virtualCard.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 10:00
 * @LastEditTime 2024-03-06 10:00
 * @Description
 */
@Getter
@AllArgsConstructor
public enum OrderStateENUM {
    NON_PAYMENT((byte)1, "未支付"),
    PAID((byte)2, "已支付");
    private final Byte code;
    private final String message;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/enums/RefundItemStateENUM.java
New file
@@ -0,0 +1,21 @@
package com.dy.pipIrrSell.virtualCard.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * @author ZhuBaoMin
 * @date 2024-03-11 8:49
 * @LastEditTime 2024-03-11 8:49
 * @Description 退款分项的退款状态,即微信退款状态
 */
@Getter
@AllArgsConstructor
public enum RefundItemStateENUM {
    NO_REFUND((byte)1, "未退款"),
    REFUNDED((byte)2, "已退款");
    private final Byte code;
    private final String message;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/virtualCard/enums/RefundStateENUM.java
New file
@@ -0,0 +1,22 @@
package com.dy.pipIrrSell.virtualCard.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * @author ZhuBaoMin
 * @date 2024-03-08 20:45
 * @LastEditTime 2024-03-08 20:45
 * @Description
 */
@Getter
@AllArgsConstructor
public enum RefundStateENUM {
    TO_AUDIT((byte)1, "待审核"),
    TO_REFUND((byte)2, "待退款"),
    REFUNDED((byte)3, "已退款");
    private final Byte code;
    private final String message;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wallet/WalletCtrl.java
@@ -76,7 +76,7 @@
        }
        if(walletSv.getWalletByClientId(clientId) != null) {
            return BaseResponseUtils.buildFail(SellResultCode.WALLET_ACCOUNT_EXIST.getMessage());
            //return BaseResponseUtils.buildFail(SellResultCode.WALLET_ACCOUNT_EXIST.getMessage());
        }
        SeWallet seWallet = new SeWallet();
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wallet/WalletSv.java
@@ -13,8 +13,8 @@
import com.dy.pipIrrGlobal.voSe.VoWalletConsume;
import com.dy.pipIrrGlobal.voSe.VoWalletRecharge;
import com.dy.pipIrrGlobal.voSe.VoWalletRefund;
import com.dy.pipIrrSell.wallet.qo.QueryVo;
import com.dy.pipIrrSell.wallet.qo.QoWalletRecharge;
import com.dy.pipIrrSell.wallet.qo.QueryVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.PojoUtils;
import org.springframework.beans.factory.annotation.Autowired;
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PayInfo.java
File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayInfo.java
@@ -1,18 +1,33 @@
package com.dy.pipirrWebChat.payment;
package com.dy.pipIrrSell.wechatpay;
/**
 * @author ZhuBaoMin
 * @date 2024-02-22 20:20
 * @LastEditTime 2024-02-22 20:20
 * @date 2024-03-06 13:49
 * @LastEditTime 2024-03-06 13:49
 * @Description
 */
public class PayInfo {
    /*
    /**
     * 小程序登录API
     */
    public static String loginUrl = "https://api.weixin.qq.com/sns/jscode2session";
    /*
    /**
     * 检验登录态
     */
    public static String checkSessionUrl = "https://api.weixin.qq.com/wxa/checksession";
    /**
     * 重置登录态
     */
    public static String resetUserSessionKeyUrl = "https://api.weixin.qq.com/wxa/resetusersessionkey";
    /**
     * 获取接口调用凭据
     */
    public static String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
    /**
     * 统一下单API
     */
    //public static String orderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
@@ -26,17 +41,18 @@
    /*
     * 支付结果通知API
     */
    public static String notifyUrl = "https://www.muxiaobao.com/api/Payment/OrderNotify";
    //public static String notifyUrl = "https://www.muxiaobao.com/api/Payment/OrderNotify";
    public static String notifyUrl = "https://44978f7456.imdo.co/webchat/payment/orderNotify";
    /*
     * 查询订单API
     */
    public static String queryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
    /*
    /**
     * 申请退款API
     */
    public static String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    public static String refundUrl = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    /*
     * 退款通知API
@@ -128,7 +144,9 @@
    /**
     * 私钥文件路径
     */
    public static String certFileName = "C:\\webchat\\apiclient_key.pem";
    public static String privateCertFileName = "C:\\webchat\\apiclient_key.pem";
    public static String publicCertFileName = "C:\\webchat\\wxp_cert.pem";
    /*
     * 微信订单号,优先使用
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PaymentCtrl.java
New file
@@ -0,0 +1,514 @@
package com.dy.pipIrrSell.wechatpay;
import com.alibaba.fastjson2.JSONObject;
import com.dy.common.aop.SsoAop;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.common.webUtil.ResultCodeMsg;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefund;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem;
import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard;
import com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState;
import com.dy.pipIrrGlobal.voSe.VoClient;
import com.dy.pipIrrSell.client.ClientSv;
import com.dy.pipIrrSell.result.SellResultCode;
import com.dy.pipIrrSell.util.AesUtil;
import com.dy.pipIrrSell.util.PayHelper;
import com.dy.pipIrrSell.util.RestTemplateUtil;
import com.dy.pipIrrSell.virtualCard.VirtualCardSv;
import com.dy.pipIrrSell.virtualCard.dto.DtoVirtualCard;
import com.dy.pipIrrSell.virtualCard.enums.LastOperateENUM;
import com.dy.pipIrrSell.virtualCard.enums.RefundItemStateENUM;
import com.dy.pipIrrSell.wallet.enums.RefundStatusENUM;
import com.dy.pipIrrSell.wechatpay.dto.Code2Session;
import com.dy.pipIrrSell.wechatpay.dto.DtoOrder;
import com.dy.pipIrrSell.wechatpay.dto.OrderNotify;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 13:49
 * @LastEditTime 2024-03-06 13:49
 * @Description
 */
@Slf4j
@Tag(name = "微信支付管理", description = "微信支付各种操作")
@RestController
@RequestMapping(path="payment")
@RequiredArgsConstructor
public class PaymentCtrl {
    private final PaymentSv paymentSv;
    private final RestTemplateUtil restTemplateUtil;
    private final PayHelper payHelper;
    private final VirtualCardSv virtualCardSv;
    private final ClientSv clientSv;
    private final String privateCertFileName = PayInfo.privateCertFileName;
    private final String appid = PayInfo.appid;
    private final String mchid = PayInfo.mchid;
    private final String schema = PayInfo.schema;
    private final String signType = PayInfo.signType;
    private final String description = PayInfo.description;
    private final String loginUrl = PayInfo.loginUrl;
    private final String notifyUrl = PayInfo.notifyUrl;
    private final String grantType = PayInfo.grantType;
    // 平台证书公钥
    private final Map CERTIFICATE_MAP = new HashMap();
    /**
     * 登录凭证校验
     * @param code2Session 登录凭证校验传入对象
     * @param bindingResult
     * @return
     * @throws Exception
     */
    @Operation(summary = "登录凭证校验", description = "登录凭证校验")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @PostMapping(path = "getSessionId")
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<Boolean> getSessionId(@RequestBody @Valid Code2Session code2Session, BindingResult bindingResult) throws Exception {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("appid", appid);
        queryParams.put("secret", code2Session.getSecret());
        queryParams.put("js_code", code2Session.getJs_code());
        queryParams.put("grant_type", grantType);
        Map<String, String> headerParams = new HashMap<>();
        JSONObject job = restTemplateUtil.get(loginUrl, queryParams, headerParams);
        if(job.getLong("errcode") != null && job.getLong("errcode") >= -1) {
            return BaseResponseUtils.buildFail("登录凭证校验失败");
        }
        String openid = job.getString("openid");
        String sessionKey = job.getString("session_key");
        // 检验登录态
        //JSONObject checkSessionKey = payHelper.checkSessionKey(appid, secret, openid, sessionKey);
        //if(checkSessionKey != null) {
        //    Integer errcode = checkSessionKey.getInteger("errcode");
        //    String errmsg = checkSessionKey.getString("errmsg");
        //}
        // 重置登录态
        //JSONObject resetUserSessionKey = payHelper.resetUserSessionKey(appid, secret, openid, sessionKey);
        //if(resetUserSessionKey != null) {
        //    Integer errcode = checkSessionKey.getInteger("errcode");
        //    String errmsg = checkSessionKey.getString("errmsg");
        //    String openid_New = checkSessionKey.getString("openid");
        //    String sessionKey_New = checkSessionKey.getString("session_key");
        //}
        // 添加登录态记录
        SeWebchatLogonState po = new SeWebchatLogonState();
        po.setOpenId(openid);
        po.setSessionKey(sessionKey);
        Date createTime = new Date();
        po.setCreateTime(createTime);
        Long id = paymentSv.insert(po);
        if(id == null || id <= 0) {
            return BaseResponseUtils.buildFail("登录态记录添加失败");
        }
        String SessionId = String.valueOf(id);
        return BaseResponseUtils.buildSuccess(SessionId) ;
    }
    /**
     * 下载微信支付平台证书 测试完废除
     * @return
     * @throws Exception
     */
    @Operation(summary = "下载平台证书", description = "下载平台证书")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @GetMapping(path = "certificates")
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<Boolean> certificates() throws Exception {
        String method = "GET";
        String httpUrl = "/v3/certificates";
        String nonceStr = payHelper.generateRandomString();
        Long timestamp = System.currentTimeMillis() / 1000;
        String header = schema + " " + payHelper.getToken(method, httpUrl, "", nonceStr, timestamp, privateCertFileName);
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", header);
        headers.put("Accept", "application/json");
        JSONObject job_result = restTemplateUtil.getHeaders(PayInfo.certificates,null, headers);
        JSONObject job_headers = job_result.getJSONObject("headers");
        String wechatpayNonce = job_headers.getJSONArray("Wechatpay-Nonce").getString(0);
        String wechatpaySerial = job_headers.getJSONArray("Wechatpay-Serial").getString(0);
        String wechatpaySignature = job_headers.getJSONArray("Wechatpay-Signature").getString(0);
        String wechatpaySignatureType = job_headers.getJSONArray("Wechatpay-Signature-Type").getString(0);
        String wechatpayTimestamp = job_headers.getJSONArray("Wechatpay-Timestamp").getString(0);
        JSONObject job_body = job_result.getJSONObject("body");
        // 构造验签名串
        String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, job_body.toJSONString());
        // 验证签名
        Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature);
        return BaseResponseUtils.buildSuccess();
    }
    /**
     * JSAPI下单
     * @param order 下单请求对象,包含需要传入的参数
     * @param bindingResult
     * @return
     */
    @Operation(summary = "JSAPI下单", description = "JSAPI下单")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @PostMapping(path = "placeOrder")
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<Boolean> placeOrder(@RequestBody @Valid DtoOrder order, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        // 接收参数:登录态ID、农户ID、虚拟卡ID、充值金额
        String sessionId = order.getSessionId();
        Long virtualId = order.getVirtualId();
        Integer rechargeAmount = order.getRechargeAmount();
        String prepayId = "";
        SeWebchatLogonState po = paymentSv.selectOne(Long.parseLong(sessionId));
        String openid = po.getOpenId();
        SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(virtualId);
        Long clientId = seVirtualCard.getClientId();
        VoClient voClient = clientSv.getOneClient(clientId);
        String clientNum = voClient.getClientNum();
        // 生成订单号并添加充值记录
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String orderNumber = clientNum + dateFormat.format(new Date());
        // 生成虚拟卡充值记录(部分字段)
        DtoVirtualCard virtualCard = new DtoVirtualCard();
        virtualCard.setOrderNumber(orderNumber);
        virtualCard.setClientId(clientId);
        virtualCard.setVirtualId(virtualId);
        virtualCard.setRechargeAmount(rechargeAmount);
        BaseResponse result = virtualCardSv.insertVCRecharge(virtualCard);
        if(!result.getCode().equals("0001")) {
            return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_ADD_FAIL.getMessage());
        }
        JSONObject job_body = new JSONObject();
        job_body.put("appid", appid);
        job_body.put("mchid", mchid);
        job_body.put("description", description);
        job_body.put("out_trade_no", orderNumber);
        job_body.put("notify_url", notifyUrl);
        //订单金额
        JSONObject job_amount = new JSONObject();
        job_amount.put("total", 1);
        job_amount.put("currency", "CNY");
        job_body.put("amount", job_amount);
        //支付者
        JSONObject job_payer = new JSONObject();
        job_payer.put("openid", openid);
        job_body.put("payer", job_payer);
        // 获取随机串和时间戳,放在此处以保证
        String nonceStr = payHelper.generateRandomString();
        Long timestamp = System.currentTimeMillis() / 1000;
        String method = "POST";
        String httpUrl = "/v3/pay/transactions/jsapi";
        String body = job_body.toJSONString();
        String header = schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName);
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", header);
        headers.put("Accept", "application/json");
        headers.put("Content-Type", "application/json");
        // 暂时注释掉,认证通过后再放开
        //JSONObject job_result = restTemplateUtil.post(PayInfo.orderUrl, body, headers);
        //if(job_result != null) {
        //    System.out.println(job_result.toString());
        //    prepayId = job_result.getString("prepay_id");
        //}
        return BaseResponseUtils.buildSuccess(prepayId) ;
    }
    /**
     * 支付通知/退款结果通知
     * @param headers
     * @param orderNotify
     * @param response
     * @return
     * @throws IOException
     * @throws GeneralSecurityException
     */
    @Operation(summary = "支付通知", description = "支付通知")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @PostMapping(path = "orderNotify", consumes = MediaType.APPLICATION_JSON_VALUE)
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public JSONObject orderNotify(@RequestHeader HttpHeaders headers, @RequestBody OrderNotify orderNotify, HttpServletResponse response) throws IOException, GeneralSecurityException {
        JSONObject result = new JSONObject();
        /**
         * 1.验签处理
         *      从header中取出4个子参数,同时取出body
         *      验时间差,超过5分钟的不处理
         *      验证签名
         *      验证书序列号,必须与某一个证书的序列号一致
         */
        String wechatpayNonce = String.valueOf(headers.get("Wechatpay-Nonce").get(0));
        String wechatpaySerial = String.valueOf(headers.get("Wechatpay-Serial").get(0));
        String wechatpaySignature = String.valueOf(headers.get("Wechatpay-Signature").get(0));
        String wechatpayTimestamp = String.valueOf(headers.get("Wechatpay-Timestamp").get(0));
        String bodyStr = JSONObject.toJSONString(orderNotify);
        // 验时间戳,时间差大于5分钟的拒绝
        Long timeDiff = (System.currentTimeMillis() / 1000 - Long.parseLong(wechatpayTimestamp))/60;
        if(timeDiff > 5) {
            response.setStatus(500);
            result.put("code", "FAIL");
            result.put("message", "失败");
            return result;
        }
        // 构造验签名串
        String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, bodyStr);
        // 验证签名
        Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature);
        if(!valid) {
            response.setStatus(500);
            result.put("code", "FAIL");
            result.put("message", "失败");
            return result;
        }
        // 序列号验证要放在验签后,因为验签时可能会下载新的证书
        boolean SerialIsValid = false;
        for (String key : payHelper.CERTIFICATE_MAP.keySet()) {
            if(key.equals(wechatpaySerial)) {
                SerialIsValid = true;
            }
        }
        if(!SerialIsValid) {
            response.setStatus(500);
            result.put("code", "FAIL");
            result.put("message", "失败");
            return result;
        }
        /**
         * 解密处理
         *      1
         */
        String eventType = orderNotify.getEvent_type();
        if(eventType != null && eventType.equals("TRANSACTION.SUCCESS")) {
            // 支付成功回调
            /**
             * 支付成功的回调
             * 取出通知数据对象,继而取出解密所需的associatedData和nonce,以及密文ciphertext
             * 解密ciphertext得到
             */
            OrderNotify.NotifyResource notifyResource = orderNotify.getResource();
            String associatedData = notifyResource.getAssociated_data();
            String nonce = notifyResource.getNonce();
            String ciphertext = notifyResource.getCiphertext();
            String resource = AesUtil.decryptToString(PayInfo.key.getBytes("utf-8"), associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
            JSONObject job_resource = JSONObject.parseObject(resource);
            // 解密后取出:商户订单员、微信支付订单号、交易状态、支付完成时间
            String out_trade_no = job_resource.getString("out_trade_no");
            String transaction_id = job_resource.getString("transaction_id");
            String trade_state = job_resource.getString("trade_state");
            Date success_time = job_resource.getDate("success_time");
            // 更新虚拟卡表及充值表响应字段
            BaseResponse result_ = virtualCardSv.updateVCRecharge(out_trade_no, success_time);
            if(!result_.getCode().equals("0001")) {
                response.setStatus(500);
                result.put("code", "FAIL");
                result.put("message", "失败");
                return result;
            }
        } else if(eventType != null && eventType.equals("REFUND.SUCCESS")) {
            // 退款成功后回调
            /**
             * 退款成功的回调
             * 取出通知数据对象,继而取出解密所需的associatedData和nonce,以及密文ciphertext
             * 解密ciphertext得到
             */
            OrderNotify.NotifyResource notifyResource = orderNotify.getResource();
            String associatedData = notifyResource.getAssociated_data();
            String nonce = notifyResource.getNonce();
            String ciphertext = notifyResource.getCiphertext();
            String resource = AesUtil.decryptToString(PayInfo.key.getBytes("utf-8"), associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
            JSONObject job_resource = JSONObject.parseObject(resource);
            // 解密后取出:商户订单员、微信支付订单号、交易状态、支付完成时间
            String out_trade_no = job_resource.getString("out_trade_no");
            String transaction_id = job_resource.getString("transaction_id");
            String out_refund_no = job_resource.getString("out_refund_no");
            String refund_status = job_resource.getString("refund_status");
            Date success_time = job_resource.getDate("success_time");
            if(!refund_status.equals("SUCCESS")) {
                response.setStatus(500);
                result.put("code", "FAIL");
                result.put("message", "失败");
                return result;
            }
            // 更新虚拟卡表及充值表响应字段
            SeVcRefundItem seVcRefundItem = new SeVcRefundItem();
            seVcRefundItem.setRefundTime(success_time);
            seVcRefundItem.setRefundStatus(RefundItemStateENUM.REFUNDED.getCode());
            Integer rec = virtualCardSv.updateRefundItem(seVcRefundItem);
            if(rec == null && rec <= 0) {
                response.setStatus(500);
                result.put("code", "FAIL");
                result.put("message", "失败");
                return result;
            }
            // 根据退款单号反查退款ID,根据退款ID获取退款状态是未退款的记录数量,如果是0则说明全部退款完成,更新退款表状态为已退款,将退款后金额更新到虚拟卡表
            /**
             * 根据退款通知接口返回的退款单号反查退款ID,查询该退款ID下未退款记录数量
             *      如果结果为0,则该退款已经完成
             *      1. 更新退款表状态为已退款
             *      2. 将退款后余额更新到虚拟卡表中
             */
            Integer noRefundedCount = virtualCardSv.getNoRefundedCount(out_refund_no);
            if(noRefundedCount != null && noRefundedCount == 0) {
                // 获取退款对象并修改退款状态
                Long refundId = virtualCardSv.getRefundIdByRefundNumber(out_refund_no);
                SeVcRefund seVcRefund = virtualCardSv.selectRefundByRefundId(refundId);
                seVcRefund.setRefundStatus(RefundStatusENUM.REFUNDED.getCode());
                virtualCardSv.updateRefund(seVcRefund);
                // 获取虚拟卡对象并修改余额、最后操作、最后操作时间
                Long vcId = seVcRefund.getVcId();
                Double afterRefund = seVcRefund.getAfterRefund();
                SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(vcId);
                seVirtualCard.setMoney(afterRefund);
                seVirtualCard.setLastOperate(LastOperateENUM.REFUND.getCode());
                seVirtualCard.setLastOperateTime(new Date());
                virtualCardSv.updateVirtualCard(seVirtualCard);
            }
        }
        // 通知应答
        response.setStatus(200);
        result.put("code", "SUCCESS");
        result.put("message", "成功");
        return  result;
    }
    /**
     * 再次签名
     * @param prepayId 预支付交易会话标识
     * @return 小程序调起支付参数
     * @throws Exception
     */
    @Operation(summary = "再次签名", description = "再次签名")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @GetMapping(path = "/signAgain")
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<JSONObject> signAgain(@RequestParam("prepayId")  String prepayId) throws Exception {
        // 获取随机串和时间戳,放在此处以保证
        String appid = PayInfo.appid;
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = payHelper.generateRandomString();
        String pkg = "prepay_id=" + prepayId;
        String message = payHelper.buildMessage_signAgain(appid, timestamp, nonceStr, pkg);
        String paySign = payHelper.sign(message.getBytes("utf-8"), privateCertFileName);
        JSONObject job_result = new JSONObject();
        job_result.put("timestamp", timestamp);
        job_result.put("nonceStr", nonceStr);
        job_result.put("package", pkg);
        job_result.put("signType", signType);
        job_result.put("paySign", paySign);
        return BaseResponseUtils.buildSuccess(job_result) ;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PaymentSv.java
New file
@@ -0,0 +1,55 @@
package com.dy.pipIrrSell.wechatpay;
import com.dy.pipIrrGlobal.daoSe.SeVcRechargeMapper;
import com.dy.pipIrrGlobal.daoSe.SeWebchatLogonStateMapper;
import com.dy.pipIrrGlobal.pojoSe.SeVcRecharge;
import com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 13:51
 * @LastEditTime 2024-03-06 13:51
 * @Description
 */
@Slf4j
@Service
public class PaymentSv {
    @Autowired
    private SeWebchatLogonStateMapper seWebchatLogonStateMapper;
    @Autowired
    private SeVcRechargeMapper seVcRechargeMapper;
    /**
     * 添加登录态状态记录
     * @param po
     * @return
     */
    Long insert(SeWebchatLogonState po) {
        seWebchatLogonStateMapper.insert(po);
        return po.getId();
    }
    /**
     * 根据登录态ID获取登录态对象
     * @param id
     * @return
     */
    SeWebchatLogonState selectOne(Long id) {
        return seWebchatLogonStateMapper.selectByPrimaryKey(id);
    }
    /**
     * 添加虚拟卡充值记录
     * @param po
     * @return
     */
    Long insertVCRecharge(SeVcRecharge po) {
        seVcRechargeMapper.insert(po);
        return po.getId();
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/Code2Session.java
File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/dto/Code2Session.java
@@ -1,4 +1,4 @@
package com.dy.pipirrWebChat.payment.dto;
package com.dy.pipIrrSell.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
@@ -6,8 +6,8 @@
/**
 * @author ZhuBaoMin
 * @date 2024-02-22 15:34
 * @LastEditTime 2024-02-22 15:34
 * @date 2024-03-06 13:53
 * @LastEditTime 2024-03-06 13:53
 * @Description
 */
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/DtoOrder.java
New file
@@ -0,0 +1,40 @@
package com.dy.pipIrrSell.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 13:54
 * @LastEditTime 2024-03-06 13:54
 * @Description 下单请求对象,下单时传给JSAPI下单接口
 */
@Data
@Schema(name = "下单请求对象")
public class DtoOrder {
    public static final long serialVersionUID = 202403012108001L;
    /**
     * 登录态ID,用来获取openID
     */
    @Schema(description = "登录态ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "登录态ID不能为空")
    private String sessionId;
    /**
     * 虚拟卡编号,外键,用来获取虚拟卡余额
     */
    @Schema(description = "虚拟卡编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚拟卡编号不能为空")
    private Long virtualId;
    /**
     * 充值金额金额
     */
    @Schema(description = "支付金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚支付金额不能为空")
    private Integer rechargeAmount;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/OrderNotify.java
New file
@@ -0,0 +1,81 @@
package com.dy.pipIrrSell.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 13:54
 * @LastEditTime 2024-03-06 13:54
 * @Description 支付和退款通知接收对象
 */
@Data
@Schema(name = "支付和退款回调对象")
public class OrderNotify {
    public static final long serialVersionUID = 202402291431001L;
    /**
     * 通知ID
     */
    private String id;
    /**
     * 通知创建时间
     */
    private String create_time;
    /**
     * 通知类型
     * 支付成功通知的类型为:TRANSACTION.SUCCESS
     */
    private String event_type;
    /**
     * 通知数据类型
     * 支付成功通知为:encrypt-resource
     */
    private String resource_type;
    /**
     * 通知数据
     */
    private NotifyResource resource;
    /**
     * 回调摘要,退款通知无此属性
     */
    private String summary;
    @Data
    public class NotifyResource {
        /**
         * 加密算法类型
         * 仅支持AEAD_AES_256_GCM
         */
        private String algorithm;
        /**
         * 数据密文
         */
        private String ciphertext;
        /**
         * 附加数据
         */
        public String associated_data;
        /**
         * 原始类型
         * 原始回调类型为:transaction
         */
        private String original_type;
        /**
         * 随机串
         */
        private String nonce;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/Orders.java
New file
@@ -0,0 +1,44 @@
package com.dy.pipIrrSell.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2024-03-07 21:56
 * @LastEditTime 2024-03-07 21:56
 * @Description 订单对象,从充值表中取出的订单信息
 */
@Data
@Schema(name = "订单对象")
public class Orders {
    public static final long serialVersionUID = 202403072157001L;
    /**
     * 订单号
     */
    @Schema(description = "订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "订单号不能为空")
    private String orderNumber;
    /**
     * 充值金额
     */
    @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "充值金额不能为空")
    @Positive(message = "充值金额必须为大于0的整数")
    private Integer rechargeAmount;
    /**
     * 充值完成时间
     */
    @Schema(description = "充值完成时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "充值完成时间不能为空")
    private Date rechargeTime;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/Refund.java
New file
@@ -0,0 +1,39 @@
package com.dy.pipIrrSell.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 13:55
 * @LastEditTime 2024-03-06 13:55
 * @Description 退款请求对象,小程序调用接口时传参所用对象
 */
@Data
@Schema(name = "退款请求对象")
public class Refund {
    public static final long serialVersionUID = 202403011607001L;
    /**
     * 商户订单号
     */
    @Schema(description = "商户订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "商户订单号不能为空")
    private String tradeNo;
    /**
     * 退款单号
     */
    @Schema(description = "退款单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private String refundNo;
    /**
     * 退款金额
     */
    @Schema(description = "退款金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "退款金额不能为空")
    private Integer refund;
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/RefundRequest.java
New file
@@ -0,0 +1,70 @@
package com.dy.pipIrrSell.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 13:55
 * @LastEditTime 2024-03-06 13:55
 * @Description 退款请求对象,对象赋值完整后向微信请求退款
 */
@Data
@Schema(name = "退款请求对象")
public class RefundRequest {
    public static final long serialVersionUID = 202403011540001L;
    /**
     * 商户订单号,下单时的订单号
     */
    @Schema(description = "商户订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "商户订单号不能为空")
    private String out_trade_no;
    /**
     * 商户退款单号,订单号前加前缀“R”
     */
    @Schema(description = "商户退款单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "商户退款单号不能为空")
    private String out_refund_no;
    /**
     * 退款结果回调url,refundUrl
     */
    @Schema(description = "退款结果回调url", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "退款结果回调url不能为空")
    private String notify_url;
    /**
     * 金额信息
     */
    @Schema(description = "金额信息", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    private Amount amount;
    @Data
    public static class Amount {
        /**
         * 退款金额
         */
        @Schema(description = "退款金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
        @NotNull(message = "退款金额不能为空")
        private Integer refund;
        /**
         * 原订单金额,根据订单号查询
         */
        @Schema(description = "原订单金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
        @NotNull(message = "原订单金额不能为空")
        private Integer total;
        /**
         * 退款币种,固定为“CNY”
         */
        @Schema(description = "退款币种", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
        @NotBlank(message = "退款币种不能为空")
        private String currency;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/RefundResponse.java
New file
@@ -0,0 +1,114 @@
package com.dy.pipIrrSell.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-03-06 13:56
 * @LastEditTime 2024-03-06 13:56
 * @Description 退款申请返回的对象
 */
@Data
@Schema(name = "退款申请返回对象")
public class RefundResponse {
    public static final long serialVersionUID = 202403011431001L;
    /**
     * 微信支付退款号
     */
    private String refund_id;
    /**
     * 商户退款单号
     */
    private String out_refund_no;
    /**
     * 微信支付订单号
     */
    private String transaction_id;
    /**
     * 商户订单号
     */
    private String out_trade_no;
    /**
     * 退款渠道
     */
    private String channel;
    /**
     * 退款入账账户
     */
    private String user_received_account;
    /**
     * 退款成功时间
     */
    private String success_time;
    /**
     * 退款创建时间
     */
    private String create_time;
    /**
     * 退款状态
     */
    private String status;
    /**
     * 金额信息
     */
    private Amount amount;
    @Data
    private static class Amount {
        /**
         * 订单总金额
         */
        private Integer total;
        /**
         * 退款金额
         */
        private Integer refund;
        /**
         * 用户支付金额
         */
        private Integer payer_total;
        /**
         * 用户退款金额
         */
        private Integer payer_refund;
        /**
         * 应结退款金额
         */
        private Integer settlement_refund;
        /**
         * 应结订单金额
         */
        private Integer settlement_total;
        /**
         * 优惠退款金额
         */
        private Integer discount_refund;
        /**
         * 退款币种
         */
        private String currency;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/dto/ToRefund.java
New file
@@ -0,0 +1,35 @@
package com.dy.pipIrrSell.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-03-07 21:43
 * @LastEditTime 2024-03-07 21:43
 * @Description 待退款对象,用户选择一个虚拟卡退款时,系统遍历出一个退款对象列表,由多个订单共同完成退款
 */
@Data
@Schema(name = "待退款对象")
public class ToRefund {
    public static final long serialVersionUID = 202403072144001L;
    /**
     * 订单号
     */
    @Schema(description = "订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "订单号不能为空")
    private String orderNumber;
    /**
     * 退款金额
     */
    @Schema(description = "退款金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "退款金额不能为空")
    @Positive(message = "退款金额必须为大于0的整数")
    private Integer refundAmount;
}
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/.gitignore
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/.mvn/wrapper/maven-wrapper.jar
Binary files differ
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/.mvn/wrapper/maven-wrapper.properties
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/mvnw
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/mvnw.cmd
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/pom.xml
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/PipIrrWebChatApplication.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateConfig.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/WebFilterConfiguration.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/WebListenerConfiguration.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayHelper.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentCtrl.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentSv.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/result/WebChatResultCode.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/ICallback.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/OkHttpUtil.java
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/application.yml
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/log4j2.yml
File was deleted
pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/test/java/com/dy/pipirrWebChat/PipIrrWebWebchatApplicationTests.java
File was deleted
pipIrr-platform/pipIrr-web/pom.xml
@@ -26,7 +26,6 @@
        <module>pipIrr-web-gis</module>
        <module>pipIrr-web-sell</module>
        <module>pipIrr-web-project</module>
        <module>pipIrr-web-webchat</module>
    </modules>
    <dependencies>