Administrator
2024-07-16 8bcb47b7ea268061f4629e0953de4e3597fca690
2024-07-16 朱宝民
19个文件已修改
8个文件已添加
1014 ■■■■■ 已修改文件
pipIrr-platform/pipIrr-common/src/main/java/com/dy/common/webFilter/WXDataSourceNameSetFilter.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/command/ComSupport.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoRm/RmCommandHistory.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoCommand.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoUnclosedValve.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voSe/VoVirtualCard.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/application-database-sp.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/application-database-ym.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/RmCommandHistoryMapper.xml 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeClientMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/common/CommandSv.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/valve/ValveCtrl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PayInfo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/CommandSv.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/ValveCtrl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/result/WechatResultCode.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/virtualCard/SeClientToVoClient.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/virtualCard/VirtualCardCtrl.java 85 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/PayInfo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/PaymentCtrl.java 511 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/PaymentSv.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/dto/Code2Session.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/dto/DtoOrder.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/dto/NotifyResource.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/dto/OrderNotify.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/enums/RefundStatusENUM.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-common/src/main/java/com/dy/common/webFilter/WXDataSourceNameSetFilter.java
@@ -22,6 +22,9 @@
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String ds = DataSourceContext.get();
        System.out.println(ds);
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        String wxDataSourceName = httpRequest.getHeader("tag");
        if(wxDataSourceName != null && wxDataSourceName.trim().length() > 0){
@@ -30,6 +33,7 @@
            DataSourceContext.set(wxDataSourceName);
        } else {
            log.info("用户未选择数据源");
            DataSourceContext.set("ym");
        }
        filterChain.doFilter(servletRequest, servletResponse);
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/command/ComSupport.java
@@ -143,7 +143,7 @@
     */
    protected RmCommandHistory getComHistory(Long comId, String commandCode, String commandName, Long intakeId, String rtuAddr, String protocol, Object param, Long operator ) {
        RmCommandHistory rmCommandHistory = new RmCommandHistory();
        rmCommandHistory.setId(comId);
        rmCommandHistory.setComId(comId);
        rmCommandHistory.setCommandCode(commandCode);
        //rmCommandHistory.setCommandName(CodeV202404.getCodeName(commandCode));
        rmCommandHistory.setCommandName(commandName);
@@ -259,13 +259,13 @@
            // 创建命令日志对象并添加到数据库中
            RmCommandHistory rmCommandHistory = getComHistory(comId, commandCode, commandName, intakeId, rtuAddr, protocol, param, operator);
            rmCommandHistoryMapper.insert(rmCommandHistory);
            comId = rmCommandHistory.getId();
            comId = rmCommandHistory.getComId();
            // 回调异常
            if(!response_CallBack.getCode().equals("0001")) {
                // 命令日志执行结果改为失败
                rmCommandHistory = new RmCommandHistory();
                rmCommandHistory.setId(comId);
                rmCommandHistory.setComId(comId);
                rmCommandHistory.setResult((byte)0);
                rmCommandHistoryMapper.updateByPrimaryKeySelective(rmCommandHistory);
                return BaseResponseUtils.buildErrorMsg(response_CallBack.getContent().toString());
@@ -280,7 +280,7 @@
                 */
                rmCommandHistory = new RmCommandHistory();
                rmCommandHistory.setId(comId);
                rmCommandHistory.setComId(comId);
                rmCommandHistory.setResult((byte) 0);
                rmCommandHistoryMapper.updateByPrimaryKeySelective(rmCommandHistory);
                return BaseResponseUtils.buildErrorMsg(CommandResultCode.GET_RESULT_IN_ONE_MINUTE.getMessage());
@@ -298,7 +298,7 @@
                 * 更新执行结果、返回结果时间、结果内容
                 */
                rmCommandHistory = new RmCommandHistory();
                rmCommandHistory.setId(comId);
                rmCommandHistory.setComId(comId);
                rmCommandHistory.setResult((byte)0);
                rmCommandHistory.setResultTime(new Date());
                rmCommandHistory.setResultText((JSONObject)JSON.toJSON(response_CallBack.getContent()));
@@ -330,7 +330,7 @@
            // 更新命令日志:执行结果、返回结果时间、结果内容
            rmCommandHistory = new RmCommandHistory();
            rmCommandHistory.setId(comId);
            rmCommandHistory.setComId(comId);
            rmCommandHistory.setResult((byte)1);
            rmCommandHistory.setResultTime(new Date());
            rmCommandHistory.setResultText((JSONObject)JSON.toJSON(myData));
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoRm/RmCommandHistory.java
@@ -40,7 +40,7 @@
     */
    @JSONField(serializeUsing = ObjectWriterImplToString.class)
    @TableId(type = IdType.INPUT)
    private Long id;
    private Long comId;
    /**
     * 功能码
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoCommand.java
@@ -19,7 +19,7 @@
    private static final long serialVersionUID = 1L;
    @JSONField(serializeUsing= ObjectWriterImplToString.class)
    private Long id;
    private Long comId;
    private String commandName;
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoUnclosedValve.java
@@ -3,8 +3,6 @@
import com.dy.common.po.BaseEntity;
import lombok.Data;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2024-05-24 16:30
@@ -16,7 +14,7 @@
public class VoUnclosedValve implements BaseEntity {
    private static final long serialVersionUID = 202405241634001L;
    private String commandCode;
    //private String commandCode;
    private String intakeNum;
@@ -28,5 +26,5 @@
    private String vcNum;
    private Date openTime;
    //private Date openTime;
}
pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voSe/VoVirtualCard.java
@@ -1,5 +1,7 @@
package com.dy.pipIrrGlobal.voSe;
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.writer.ObjectWriterImplToString;
import com.dy.common.po.BaseEntity;
import lombok.Data;
@@ -14,9 +16,11 @@
public class VoVirtualCard implements BaseEntity {
    private static final long serialVersionUID = 202405240815001L;
    private String id;
    @JSONField(serializeUsing= ObjectWriterImplToString.class)
    private Long id;
    private String vcNum;
    @JSONField(serializeUsing= ObjectWriterImplToString.class)
    private Long vcNum;
    private Double money;
pipIrr-platform/pipIrr-global/src/main/resources/application-database-sp.yml
@@ -6,8 +6,8 @@
      type: com.alibaba.druid.pool.DruidDataSource
      driverClassName: com.mysql.cj.jdbc.Driver
#      url: jdbc:mysql://192.168.40.166:3306/pipIrr_sp?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
#      url: jdbc:mysql://127.0.0.1:3306/pipIrr_sp?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
      url: jdbc:mysql://8.140.179.55:3306/pipIrr_sp?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
      url: jdbc:mysql://127.0.0.1:3306/pipIrr_sp?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
#      url: jdbc:mysql://8.140.179.55:3306/pipIrr_sp?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
      username: root
      password: dysql,;.abc!@#
      druid:
pipIrr-platform/pipIrr-global/src/main/resources/application-database-ym.yml
@@ -5,8 +5,8 @@
            #name: ym
            type: com.alibaba.druid.pool.DruidDataSource
            driverClassName: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://192.168.40.166:3306/pipIrr_ym?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
#            url: jdbc:mysql://127.0.0.1:3306/pipIrr_ym?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
#            url: jdbc:mysql://192.168.40.166:3306/pipIrr_ym?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
            url: jdbc:mysql://127.0.0.1:3306/pipIrr_ym?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
#            url: jdbc:mysql://8.140.179.55:3306/pipIrr_ym?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
            username: root
            password: dysql,;.abc!@#
pipIrr-platform/pipIrr-global/src/main/resources/mapper/RmCommandHistoryMapper.xml
@@ -4,7 +4,7 @@
  <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoRm.RmCommandHistory">
    <!--@mbg.generated-->
    <!--@Table rm_command_history-->
    <id column="id" jdbcType="BIGINT" property="id" />
    <id column="com_id" jdbcType="BIGINT" property="comId" />
    <result column="command_code" jdbcType="VARCHAR" property="commandCode" />
    <result column="command_name" jdbcType="VARCHAR" property="commandName" />
    <result column="intake_id" jdbcType="BIGINT" property="intakeId" />
@@ -19,7 +19,7 @@
  </resultMap>
  <sql id="Base_Column_List">
    <!--@mbg.generated-->
    id, command_code, command_name, intake_id, rtu_addr, protocol, param, send_time, `operator`,
    com_id, command_code, command_name, intake_id, rtu_addr, protocol, param, send_time, `operator`,
    `result`, result_time, result_text
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
@@ -27,20 +27,20 @@
    select 
    <include refid="Base_Column_List" />
    from rm_command_history
    where id = #{id,jdbcType=BIGINT}
    where com_id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    <!--@mbg.generated-->
    delete from rm_command_history
    where id = #{id,jdbcType=BIGINT}
    where com_id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoRm.RmCommandHistory">
    <!--@mbg.generated-->
    insert into rm_command_history (id, command_code, command_name,
    insert into rm_command_history (com_id, command_code, command_name,
      intake_id, rtu_addr, protocol, param,
      send_time, `operator`, `result`, 
      result_time, result_text)
    values (#{id,jdbcType=BIGINT}, #{commandCode,jdbcType=VARCHAR}, #{commandName,jdbcType=VARCHAR}, #{intakeId,jdbcType=BIGINT},
    values (#{comId,jdbcType=BIGINT}, #{commandCode,jdbcType=VARCHAR}, #{commandName,jdbcType=VARCHAR}, #{intakeId,jdbcType=BIGINT},
      #{rtuAddr,jdbcType=VARCHAR}, #{protocol,jdbcType=VARCHAR}, #{param,jdbcType= JAVA_OBJECT, typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler},
      #{sendTime,jdbcType=TIMESTAMP}, #{operator,jdbcType=BIGINT}, #{result,jdbcType=TINYINT}, 
      #{resultTime,jdbcType=TIMESTAMP}, #{resultText,jdbcType= JAVA_OBJECT, typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler})
@@ -49,8 +49,8 @@
    <!--@mbg.generated-->
    insert into rm_command_history
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      <if test="comId != null">
        com_id,
      </if>
      <if test="commandCode != null">
        command_code,
@@ -87,8 +87,8 @@
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=BIGINT},
      <if test="comId != null">
        #{comId,jdbcType=BIGINT},
      </if>
      <if test="commandCode != null">
        #{commandCode,jdbcType=VARCHAR},
@@ -163,7 +163,7 @@
        result_text = #{resultText,jdbcType= JAVA_OBJECT, typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
    where com_id = #{comId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoRm.RmCommandHistory">
    <!--@mbg.generated-->
@@ -179,19 +179,17 @@
      `result` = #{result,jdbcType=TINYINT},
      result_time = #{resultTime,jdbcType=TIMESTAMP},
      result_text = #{resultText,jdbcType= JAVA_OBJECT, typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler}
    where id = #{id,jdbcType=BIGINT}
    where com_id = #{comId,jdbcType=BIGINT}
  </update>
  <!--根据操作员ID获取未关阀记录(包含在线情况)-->
  <select id="getUnclosedValves" resultType="com.dy.pipIrrGlobal.voRm.VoUnclosedValve">
    SELECT
        com.command_code AS commandCode,
        inta.name AS intakeNum,
        rtus.isOnLine,
        com.rtu_addr AS rtuAddr,
        com.param ->>'$.orderNo' AS orderNo,
        com.param ->>'$.icCardNo' AS vcNum,
        com.send_time AS openTime
        (SELECT param ->>'$.orderNo' AS orderNo FROM rm_command_history WHERE rtu_addr = com.rtu_addr ORDER BY send_time desc LIMIT 0,1) AS orderNo
    FROM rm_command_history com
        INNER JOIN pr_controller con ON com.rtu_addr = con.rtuAddr
        INNER JOIN pr_intake inta ON con.intakeId = inta.id
@@ -214,7 +212,7 @@
            AND param ->>'$.orderNo' = com.param ->>'$.orderNo'
      )
    </where>
    ORDER BY com.send_time DESC
    GROUP BY inta.name, rtus.isOnLine, com.rtu_addr, com.param ->>'$.icCardNo'
  </select>
  <!--根据取水口ID获取该取水口未关阀参数-->
@@ -270,7 +268,7 @@
  <!--根据指定条件获取命令日志历史记录-->
  <select id="getCommandHistories" resultType="com.dy.pipIrrGlobal.voRm.VoCommand">
    SELECT
      his.id,
      his.com_id AS comId,
      his.command_name AS commandName,
      inta.name AS intakeName,
      his.rtu_addr AS rtuAddr,
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeClientMapper.xml
@@ -396,7 +396,7 @@
  <!--根据农户编号获取5级行政区划串areaCode,补卡过程中开新卡使用-->
  <select id="getAreaCodeById" resultType="java.lang.Long">
    SELECT districtNum FROM se_client WHERE id = ${clientId}
    SELECT districtNum FROM se_client WHERE id = #{clientId}
  </select>
  <!--根据农户编号获取农户ID-->
pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml
@@ -170,7 +170,8 @@
  <!--获取农户全部虚拟卡-->
  <select id="getVCs" resultType="com.dy.pipIrrGlobal.voSe.VoVirtualCard">
    SELECT
      CAST(id AS char) AS id,
<!--      CAST(id AS char) AS id,-->
      id,
      vc_num AS vcNum,
      money,
      in_use AS inUse,
@@ -190,7 +191,7 @@
  <!--根据虚拟卡ID获取虚拟卡对象-->
  <select id="getVcById" resultType="com.dy.pipIrrGlobal.voSe.VoVirtualCard">
    SELECT
        CAST(id AS char) AS id,
        id,
        vc_num AS vcNum,
        money,
        in_use AS inUse,
pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/common/CommandSv.java
@@ -95,7 +95,7 @@
     */
    public Long insert(RmCommandHistory po) {
        rmCommandHistoryMapper.insert(po);
        return po.getId();
        return po.getComId();
    }
    /**
pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/valve/ValveCtrl.java
@@ -145,7 +145,7 @@
            ComCd92_A2Vo param = new ComCd92_A2Vo();
            param.controllerType = controllerType;
            param.projectNo = projectNo;
            param.icCardNo = vc.getVcNum();
            param.icCardNo = vc.getVcNum().toString();
            param.waterRemain = 0.0;
            param.moneyRemain = vc.getMoney();
            param.waterPrice = waterPrice;
@@ -176,7 +176,7 @@
            // 创建视图
            Com97Vo param = new Com97Vo() ;
            param.icCardNo = vc.getVcNum();
            param.icCardNo = vc.getVcNum().toString();
            param.moneyRemain = vc.getMoney();
            param.waterPrice = waterPrice;
            param.orderNo = orderNo;
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PayInfo.java
@@ -38,10 +38,10 @@
     */
    public static String certificates = "https://api.mch.weixin.qq.com/v3/certificates";
    /*
    /**
     * 支付结果通知API
     */
    public static String notifyUrl = "https://44978f7456.imdo.co/sell/payment/orderNotify";
    public static String notifyUrl = "https://44978f7456.imdo.co/wx/payment/orderNotify";
    /*
     * 查询订单API
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/CommandSv.java
@@ -145,7 +145,7 @@
     */
    public Long insert(RmCommandHistory po) {
        rmCommandHistoryMapper.insert(po);
        return po.getId();
        return po.getComId();
    }
    /**
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/ValveCtrl.java
@@ -159,7 +159,7 @@
            ComCd92_A2Vo param = new ComCd92_A2Vo();
            param.controllerType = controllerType;
            param.projectNo = projectNo;
            param.icCardNo = vc.getVcNum();
            param.icCardNo = vc.getVcNum().toString();
            param.waterRemain = 0.0;
            param.moneyRemain = vc.getMoney();
            param.waterPrice = waterPrice;
@@ -191,7 +191,7 @@
            // 创建视图
            Com97Vo param = new Com97Vo();
            param.icCardNo = vc.getVcNum();
            param.icCardNo = vc.getVcNum().toString();
            param.moneyRemain = vc.getMoney();
            param.waterPrice = waterPrice;
            param.orderNo = orderNo;
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/result/WechatResultCode.java
@@ -49,6 +49,7 @@
    /**
     * 虚拟卡
     */
    AREA_CODE_MISTAKE(10001, "该农户行政区划异常"),
    ABNORMAL(10001, "退款异常"),
    PROCESSING(10001, "退款处理中"),
    VC_OPEN_ACCOUNT_FAIL(90002, "虚拟卡账户注册失败"),
@@ -56,7 +57,14 @@
    VIRTUAL_CARD_NOT_EXIST(90006, "虚拟卡账户不存在"),
    RECHARGE_NOT_EXIST(90006, "充值记录不存在"),
    RECHARGE_FAIL(90006, "充值失败"),
    NO_ACCOUNT(40001, "您指定的虚拟卡未注册");
    RECHARGE_ADD_FAIL(10001, "充值记录添加失败"),
    NO_ACCOUNT(40001, "您指定的虚拟卡未注册"),
    VIRTUAL_CARD_CLIENT_NOT_EXIST(40001, "虚拟卡所属农户不存在"),
    /**
     * 位置支付
     */
    SESSION_ID_ERROR(50001, "登录态ID错误");
    private final Integer code;
    private final String message;
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/virtualCard/SeClientToVoClient.java
New file
@@ -0,0 +1,29 @@
package com.dy.pipIrrWechat.virtualCard;
import com.dy.pipIrrGlobal.pojoSe.SeClient;
import com.dy.pipIrrGlobal.voSe.VoClient;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
 * @author ZhuBaoMin
 * @date 2023-12-25 15:54
 * @LastEditTime 2023-12-25 15:54
 * @Description
 */
@Mapper
public interface SeClientToVoClient {
    SeClientToVoClient INSTANCT = Mappers.getMapper(SeClientToVoClient.class);
    @Mapping(target = "name", source = "name")
    @Mapping(target = "clientNum", source = "clientnum")
    @Mapping(target = "phone", source = "phone")
    @Mapping(target = "idCard", source = "idcard")
    //@Mapping(target = "cardCount", source = "cardCount")
    @Mapping(target = "address", source = "address")
    @Mapping(target = "operateDt", source = "operatedt")
    @Mapping(target = "typeId", source = "typeid")
    VoClient po2vo(SeClient po);
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/virtualCard/VirtualCardCtrl.java
@@ -1,17 +1,16 @@
package com.dy.pipIrrWechat.virtualCard;
import com.dy.common.aop.SsoAop;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.common.webUtil.QueryResultVo;
import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard;
import com.dy.pipIrrGlobal.voSe.VoVcRecharge;
import com.dy.pipIrrGlobal.voSe.VoVirtualCard;
import com.dy.pipIrrWechat.util.PayHelper;
import com.dy.pipIrrWechat.virtualCard.enums.LastOperateENUM;
import com.dy.pipIrrWechat.result.WechatResultCode;
import com.dy.pipIrrWechat.util.PayHelper;
import com.dy.pipIrrWechat.virtualCard.dto.DtoRegist;
import com.dy.pipIrrWechat.virtualCard.dto.DtoVcRecharge;
import com.dy.pipIrrWechat.virtualCard.enums.LastOperateENUM;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@@ -43,53 +42,25 @@
    private final PayHelper payHelper;
    /**
     * 获取农户全部虚拟卡
     * @return
     */
    @GetMapping(path = "/get")
    @SsoAop()
    public BaseResponse<List<VoVirtualCard>> getVCs(Long clientId){
        try {
            List<VoVirtualCard> res = virtualCardSv.getVCs(clientId);
            return BaseResponseUtils.buildSuccess(res);
        } catch (Exception e) {
            log.error("获取支付方式记录异常", e);
            return BaseResponseUtils.buildException(e.getMessage()) ;
        }
    }
    /**
     * 根据虚拟卡ID获取虚拟卡对象
     * @param vcId
     * @return
     */
    @GetMapping(path = "/getVcById")
    @SsoAop()
    public BaseResponse<VoVirtualCard> getVcById(@RequestParam Long vcId){
        try {
            return BaseResponseUtils.buildSuccess(virtualCardSv.getVcById(vcId));
        } catch (Exception e) {
            log.error("获取支付方式记录异常", e);
            return BaseResponseUtils.buildException(e.getMessage()) ;
        }
    }
    /**
     * 虚拟卡账号注册
     * 注册虚拟卡
     * @param po
     * @param bindingResult
     * @return
     */
    @PostMapping(path = "add_vc")
    @SsoAop()
    public BaseResponse<Boolean> addVC(@RequestBody @Valid DtoRegist po, BindingResult bindingResult){
    public BaseResponse<Boolean> addVC(@RequestBody @Valid DtoRegist po, BindingResult bindingResult) {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        Long clientId = po.getClientId();
        // 获取5级行政区划串areaCode
        String areaCode = String.valueOf(virtualCardSv.getAreaCodeById(clientId));
        Long areaCodeL = virtualCardSv.getAreaCodeById(clientId);
        if(areaCodeL == null) {
            return BaseResponseUtils.buildErrorMsg(WechatResultCode.AREA_CODE_MISTAKE.getMessage());
        }
        String areaCode = String.valueOf(areaCodeL);
        /**
         * 根据行政区划串(areaCode)在虚拟卡表中针对虚拟卡编号(vcNum)进行模糊查询
         * 如果5位顺序号已经达到最大值,提示用户联系系统管理员
@@ -101,7 +72,7 @@
            Integer number = Integer.parseInt(vcNum.substring(12));
            number = number + 1;
            if(number > 65535) {
                return BaseResponseUtils.buildFail(WechatResultCode.CARD_NUMBER_OVERRUN.getMessage());
                return BaseResponseUtils.buildErrorMsg(WechatResultCode.CARD_NUMBER_OVERRUN.getMessage());
            }
            vcNum = vcNum.substring(0, 12) + String.format("%05d", number);
        } else {
@@ -118,9 +89,38 @@
        seVirtualCard.setCreateTime(new Date());
        Long rec = virtualCardSv.insertVirtualCard(seVirtualCard);
        if(rec == null) {
            return BaseResponseUtils.buildFail(WechatResultCode.VC_OPEN_ACCOUNT_FAIL.getMessage());
            return BaseResponseUtils.buildErrorMsg(WechatResultCode.VC_OPEN_ACCOUNT_FAIL.getMessage());
        }
        return BaseResponseUtils.buildSuccess(true) ;
    }
    /**
     * 获取农户全部虚拟卡
     * @return
     */
    @GetMapping(path = "/get")
    public BaseResponse<List<VoVirtualCard>> getVCs(Long clientId){
        try {
            return BaseResponseUtils.buildSuccess(virtualCardSv.getVCs(clientId));
        } catch (Exception e) {
            log.error("获取支付方式记录异常", e);
            return BaseResponseUtils.buildException(e.getMessage()) ;
        }
    }
    /**
     * 根据虚拟卡ID获取虚拟卡对象
     * @param vcId
     * @return
     */
    @GetMapping(path = "/getVcById")
    public BaseResponse<VoVirtualCard> getVcById(@RequestParam Long vcId){
        try {
            return BaseResponseUtils.buildSuccess(virtualCardSv.getVcById(vcId));
        } catch (Exception e) {
            log.error("获取支付方式记录异常", e);
            return BaseResponseUtils.buildException(e.getMessage()) ;
        }
    }
    /**
@@ -140,7 +140,6 @@
    //})
    //@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());
@@ -198,7 +197,6 @@
    //})
    //@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());
@@ -256,7 +254,6 @@
     * @return
     */
    @GetMapping(path = "/getVcRechargeRecords")
    @SsoAop()
    public BaseResponse<QueryResultVo<List<VoVcRecharge>>> getVcRechargeRecords(DtoVcRecharge dtoVcRecharge){
        try {
            QueryResultVo<List<VoVcRecharge>> res = virtualCardSv.getVcRechargeRecords(dtoVcRecharge);
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/PayInfo.java
@@ -38,10 +38,10 @@
     */
    public static String certificates = "https://api.mch.weixin.qq.com/v3/certificates";
    /*
    /**
     * 支付结果通知API
     */
    public static String notifyUrl = "https://44978f7456.imdo.co/sell/payment/orderNotify";
    public static String notifyUrl = "https://44978f7456.imdo.co/wx/payment/orderNotify";
    /*
     * 查询订单API
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/PaymentCtrl.java
New file
@@ -0,0 +1,511 @@
package com.dy.pipIrrWechat.wechatpay;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.common.webUtil.ResultCodeMsg;
import com.dy.pipIrrGlobal.pojoSe.*;
import com.dy.pipIrrGlobal.voSe.VoClient;
import com.dy.pipIrrWechat.result.WechatResultCode;
import com.dy.pipIrrWechat.util.AesUtil;
import com.dy.pipIrrWechat.util.PayHelper;
import com.dy.pipIrrWechat.util.RestTemplateUtil;
import com.dy.pipIrrWechat.virtualCard.VirtualCardSv;
import com.dy.pipIrrWechat.virtualCard.dto.DtoVirtualCard;
import com.dy.pipIrrWechat.virtualCard.enums.LastOperateENUM;
import com.dy.pipIrrWechat.virtualCard.enums.RefundItemStateENUM;
import com.dy.pipIrrWechat.wechatpay.dto.Code2Session;
import com.dy.pipIrrWechat.wechatpay.dto.DtoOrder;
import com.dy.pipIrrWechat.wechatpay.dto.NotifyResource;
import com.dy.pipIrrWechat.wechatpay.dto.OrderNotify;
import com.dy.pipIrrWechat.wechatpay.enums.RefundStatusENUM;
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.HttpServletRequest;
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.BufferedReader;
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-07-16 15:05
 * @LastEditTime 2024-07-16 15:05
 * @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 String privateCertFileName = com.dy.pipIrrWechat.wechatpay.PayInfo.privateCertFileName;
    private final String appid = com.dy.pipIrrWechat.wechatpay.PayInfo.appid;
    private final String secret = com.dy.pipIrrWechat.wechatpay.PayInfo.secret;
    private final String mchid = com.dy.pipIrrWechat.wechatpay.PayInfo.mchid;
    private final String schema = com.dy.pipIrrWechat.wechatpay.PayInfo.schema;
    private final String signType = com.dy.pipIrrWechat.wechatpay.PayInfo.signType;
    private final String description = com.dy.pipIrrWechat.wechatpay.PayInfo.description;
    private final String loginUrl = com.dy.pipIrrWechat.wechatpay.PayInfo.loginUrl;
    private final String notifyUrl = com.dy.pipIrrWechat.wechatpay.PayInfo.notifyUrl;
    private final String grantType = com.dy.pipIrrWechat.wechatpay.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)
    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());
        }
        String phoneNumber = code2Session.getPhoneNumber();
        String jsCode = code2Session.getJs_code();
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("appid", appid);
        queryParams.put("secret", secret);
        queryParams.put("js_code", jsCode);
        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");
        Long clientId = paymentSv.getClientIdByPhone(phoneNumber);
        String SessionId = "";
        if(clientId != null) {
            // 添加微信用户账户记录
            SeOpenId seOpenId = new SeOpenId();
            seOpenId.setClientId(clientId);
            seOpenId.setOpenId(openid);
            seOpenId.setSessionKey(sessionKey);
            seOpenId.setCreateTime(new Date());
            Long rec = paymentSv.addOpenId(seOpenId);
            if(rec != null) {
                SessionId = String.valueOf(rec);
            }
            return BaseResponseUtils.buildSuccess(SessionId);
        } else {
            return BaseResponseUtils.buildError(WechatResultCode.PHONE_NUMBER_IS_ERROR.getMessage());
        }
    }
    /**
     * 下载微信支付平台证书 测试完废除
     * @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)
    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(com.dy.pipIrrWechat.wechatpay.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 预支付交易会话标识(有效期2小时)
     */
    @PostMapping(path = "placeOrder")
    @Transactional(rollbackFor = Exception.class)
    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、充值金额(分)
        Long sessionId = order.getSessionId();
        Long virtualId = order.getVcId();
        Integer rechargeAmount = order.getRechargeAmount();
        String prepayId = "";
        SeOpenId po = paymentSv.selectOne(sessionId);
        if(po == null) {
            return BaseResponseUtils.buildErrorMsg(WechatResultCode.SESSION_ID_ERROR.getMessage());
        }
        String openid = po.getOpenId();
        SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(virtualId);
        if(seVirtualCard == null) {
            return BaseResponseUtils.buildErrorMsg(WechatResultCode.VIRTUAL_CARD_NOT_EXIST.getMessage());
        }
        Long clientId = seVirtualCard.getClientId();
        VoClient voClient = paymentSv.getOneClient(clientId);
        if(voClient == null) {
            return BaseResponseUtils.buildErrorMsg(WechatResultCode.VIRTUAL_CARD_CLIENT_NOT_EXIST.getMessage());
        }
        String clientNum = voClient.getClientNum();
        // 生成订单号并添加充值记录
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String orderNumber = clientNum + dateFormat.format(new Date());
        // 生成虚拟卡充值记录(部分字段)
        DtoVirtualCard virtualCard = new DtoVirtualCard();
        virtualCard.setVirtualId(virtualId);
        virtualCard.setClientId(clientId);
        virtualCard.setOrderNumber(orderNumber);
        virtualCard.setRechargeAmount(rechargeAmount);
        BaseResponse result = virtualCardSv.insertVCRecharge(virtualCard);
        if(!result.getCode().equals("0001")) {
            return BaseResponseUtils.buildErrorMsg(WechatResultCode.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", rechargeAmount);
        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(com.dy.pipIrrWechat.wechatpay.PayInfo.orderUrl, body, headers);
        if(job_result == null) {
            return BaseResponseUtils.buildFail(WechatResultCode.RECHARGE_ADD_FAIL.getMessage());
        }
        return BaseResponseUtils.buildSuccess(job_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)
    public BaseResponse<JSONObject> signAgain(@RequestParam("prepayId") String prepayId) throws Exception {
        // 获取随机串和时间戳,放在此处以保证
        String appid = com.dy.pipIrrWechat.wechatpay.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) ;
    }
    /**
     * 支付通知/退款结果通知
     * @param headers
     * @param request
     * @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)
    public JSONObject orderNotify(@RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException {
        JSONObject result = new JSONObject();
        /**
         * 1.验签处理
         *      从header中取出4个子参数
         *      验时间差,超过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));
        // 获取body内容
        BufferedReader reader = request.getReader();
        StringBuilder stringBuilder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
        String bodyStr = stringBuilder.toString();
        // body转对象
        OrderNotify orderNotify = JSON.parseObject(bodyStr, OrderNotify.class);
        // 验时间戳,时间差大于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;
        }
        // 解密处理
        String eventType = orderNotify.getEvent_type();
        if(eventType != null && eventType.equals("TRANSACTION.SUCCESS")) {
            // 支付成功回调
            /**
             * 支付成功的回调
             * 取出通知数据对象,继而取出解密所需的associatedData和nonce,以及密文ciphertext
             * 解密ciphertext得到
             */
            NotifyResource notifyResource = orderNotify.getResource();
            String associatedData = notifyResource.getAssociated_data();
            String nonce = notifyResource.getNonce();
            String ciphertext = notifyResource.getCiphertext();
            String resource = AesUtil.decryptToString(com.dy.pipIrrWechat.wechatpay.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");
            // 如果当前订单状态为未支付状态,则更新虚拟卡表及充值表响应字段
            SeVcRecharge seVcRecharge = virtualCardSv.getVCRechargeByorderNumber(out_trade_no);
            if(seVcRecharge != null && seVcRecharge.getOrderState() == 1) {
                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得到
             */
            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;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/PaymentSv.java
New file
@@ -0,0 +1,83 @@
package com.dy.pipIrrWechat.wechatpay;
import com.dy.pipIrrGlobal.daoSe.SeClientMapper;
import com.dy.pipIrrGlobal.daoSe.SeOpenIdMapper;
import com.dy.pipIrrGlobal.daoSe.SeVcRechargeMapper;
import com.dy.pipIrrGlobal.pojoSe.SeClient;
import com.dy.pipIrrGlobal.pojoSe.SeOpenId;
import com.dy.pipIrrGlobal.pojoSe.SeVcRecharge;
import com.dy.pipIrrGlobal.voSe.VoClient;
import com.dy.pipIrrWechat.virtualCard.SeClientToVoClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * @author ZhuBaoMin
 * @date 2024-07-16 15:03
 * @LastEditTime 2024-07-16 15:03
 * @Description
 */
@Slf4j
@Service
public class PaymentSv {
    @Autowired
    private SeVcRechargeMapper seVcRechargeMapper;
    @Autowired
    private SeOpenIdMapper seOpenIdMapper;
    @Autowired
    private SeClientMapper seClientMapper;
    /**
     * 根据登录态ID获取登录态对象
     * @param sessionId
     * @return
     */
    SeOpenId selectOne(Long sessionId) {
        return seOpenIdMapper.selectByPrimaryKey(sessionId);
    }
    /**
     * 添加虚拟卡充值记录
     * @param po
     * @return
     */
    Long insertVCRecharge(SeVcRecharge po) {
        seVcRechargeMapper.insert(po);
        return po.getId();
    }
    /**
     * 根据主键获取农户对象
     * @param id 农户主键
     * @return 农户对象
     */
    public VoClient getOneClient(Long id) {
        SeClient seClient = seClientMapper.selectByPrimaryKey(id);
        VoClient voClient = SeClientToVoClient.INSTANCT.po2vo(seClient);
        return voClient;
    }
    /**
     * 根据电话号码获取农户ID
     * @param phoneNumber
     * @return
     */
    public Long getClientIdByPhone(String phoneNumber) {
        return seClientMapper.getClientIdByPhone(phoneNumber);
    }
    /**
     * 添加微信用户账户记录
     * @param po
     * @return
     */
    public Long addOpenId(SeOpenId po) {
        seOpenIdMapper.insert(po);
        //return po.getClientId();
        return po.getId();
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/dto/Code2Session.java
New file
@@ -0,0 +1,45 @@
package com.dy.pipIrrWechat.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-07-16 15:13
 * @LastEditTime 2024-07-16 15:13
 * @Description
 */
@Data
@Schema(name = "登录凭证校验传入对象")
public class Code2Session {
    public static final long serialVersionUID = 202402221335001L;
    /**
     * 小程序 appId
     */
    @Schema(description = "小程序 appId", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "小程序 appId不能为空")
    private String appid;
    /**
     * 小程序 appSecret
     */
    @Schema(description = "小程序 appSecret", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "小程序 appSecret不能为空")
    private String secret;
    /**
     * js_code
     */
    @Schema(description = "js_code", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotBlank(message = "js_code不能为空")
    private String js_code;
    /**
     * 手机号
     */
    @NotBlank(message = "手机号不能为空")
    private String phoneNumber;
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/dto/DtoOrder.java
New file
@@ -0,0 +1,39 @@
package com.dy.pipIrrWechat.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-07-16 15:14
 * @LastEditTime 2024-07-16 15:14
 * @Description
 */
@Data
@Schema(name = "下单请求对象")
public class DtoOrder {
    public static final long serialVersionUID = 202403012108001L;
    /**
     * 登录态ID,用来获取openID
     */
    @Schema(description = "登录态ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "登录态ID不能为空")
    private Long sessionId;
    /**
     * 虚拟卡ID,用来获取虚拟卡余额
     */
    @Schema(description = "虚拟卡编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚拟卡ID不能为空")
    private Long vcId;
    /**
     * 充值金额金额
     */
    @Schema(description = "支付金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
    @NotNull(message = "虚支付金额不能为空")
    private Integer rechargeAmount;
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/dto/NotifyResource.java
New file
@@ -0,0 +1,40 @@
package com.dy.pipIrrWechat.wechatpay.dto;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-07-16 15:14
 * @LastEditTime 2024-07-16 15:14
 * @Description
 */
@Data
public class NotifyResource {
    /**
     * 原始类型
     * 原始回调类型为:transaction
     */
    private String original_type;
    /**
     * 加密算法类型
     * 仅支持AEAD_AES_256_GCM
     */
    private String algorithm;
    /**
     * 数据密文
     */
    private String ciphertext;
    /**
     * 附加数据
     */
    public String associated_data;
    /**
     * 随机串
     */
    private String nonce;
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/dto/OrderNotify.java
New file
@@ -0,0 +1,50 @@
package com.dy.pipIrrWechat.wechatpay.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2024-07-16 15:15
 * @LastEditTime 2024-07-16 15:15
 * @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;
}
pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/wechatpay/enums/RefundStatusENUM.java
New file
@@ -0,0 +1,21 @@
package com.dy.pipIrrWechat.wechatpay.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * @author ZhuBaoMin
 * @date 2024-07-16 15:17
 * @LastEditTime 2024-07-16 15:17
 * @Description
 */
@Getter
@AllArgsConstructor
public enum RefundStatusENUM {
    NO_REFUND((byte)1, "未退款"),
    REFUNDED((byte)2, "已退款");
    private final Byte code;
    private final String message;
}