2024-03-12 朱宝民 增加已挂失,未补卡接口
| | |
| | | * @return |
| | | */ |
| | | List<VoCards> getCards(Map<?, ?> params); |
| | | |
| | | /** |
| | | * 获取已挂失,未补卡的记录数量,应用程序使用 |
| | | * @return |
| | | */ |
| | | Long getUnreplacedRecordCount(); |
| | | |
| | | /** |
| | | * 获取已挂失,未补卡的记录,应用程序使用 |
| | | * @return |
| | | */ |
| | | List<VoCards> getUnreplaced(Map<?, ?> params); |
| | | } |
| | |
| | | |
| | | 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 |
| | |
| | | * @return |
| | | */ |
| | | SeVcRecharge getVCRechargeByorderNumber(String orderNumber); |
| | | |
| | | /** |
| | | * 根据虚拟卡号获取订单列表 |
| | | * @param virtualId |
| | | * @return |
| | | */ |
| | | List<VoOrders> getOrders(@Param("virtualId") Long virtualId); |
| | | |
| | | /** |
| | | * 根据订单号获取充值金额 |
| | | * @param orderNumber |
| | | * @return |
| | | */ |
| | | Integer getRechargeAmountByOrderNumber(@Param("orderNumber") String orderNumber); |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | |
| | | </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> |
| | |
| | | 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> |
New file |
| | |
| | | <?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> |
New file |
| | |
| | | <?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> |
| | |
| | | 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()) ; |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | 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 ; |
| | | } |
| | | } |
| | |
| | | //@Bean(name = "wechatRestTemplate") |
| | | @Bean() |
| | | public RestTemplate restTemplate() throws Exception { |
| | | //RestTemplate restTemplate = new RestTemplate(restTemplateWechatCertConfig.wechatHttpRequestFactory()); |
| | | RestTemplate restTemplate = new RestTemplate(restTemplateWechatCertConfig.wechatHttpRequestFactory()); |
| | | // 添加拦截器 |
| | | //List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); |
| | |
| | | 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, "没有符合条件的充值数据"), |
| | |
| | | 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 java.security.cert.CertificateFactory; |
| | | import java.security.spec.InvalidKeySpecException; |
| | | import java.security.spec.PKCS8EncodedKeySpec; |
| | | import java.util.Base64; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.Random; |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | |
| | | @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(); |
| | |
| | | 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()); |
| | | } |
| | | } |
| | | } |
| | |
| | | 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.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | 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 |
| | |
| | | @Validated |
| | | public class VirtualCardCtrl { |
| | | private final VirtualCardSv virtualCardSv; |
| | | private final PayHelper payHelper; |
| | | |
| | | /** |
| | | * 虚拟卡账号注册 |
| | | * @param clientId |
| | | * @return |
| | | */ |
| | | @Operation(summary = "注册虚拟卡", description = "注册虚拟卡") |
| | | @ApiResponses(value = { |
| | | @ApiResponse( |
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 用户申请退款 |
| | | * @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) ; |
| | | } |
| | | |
| | | } |
| | |
| | | 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 org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | |
| | | |
| | | @Autowired |
| | | private SeVcRechargeMapper seVcRechargeMapper; |
| | | |
| | | @Autowired |
| | | private SeVcRefundMapper seVcRefundMapper; |
| | | |
| | | @Autowired |
| | | private SeVcRefundItemMapper seVcRefundItemMapper; |
| | | |
| | | /** |
| | | * 注册虚拟卡 |
| | |
| | | public SeVirtualCard selectVirtuCardById(Long virtualId) { |
| | | return seVirtualCardMapper.selectByPrimaryKey(virtualId); |
| | | } |
| | | |
| | | /** |
| | | * 添加虚拟卡充值记录 |
| | | * JSAPI下单后生成部分充值记录 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 修改虚拟卡充值记录 |
| | | * 修改虚拟卡充值记录(废弃) |
| | | * 微信小程序支付通知后修改:余额、充值后余额、充值完成时间 |
| | | * @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); |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | |
| | | RECHARGE((byte)2, "充值"), |
| | | CONSUME((byte)3, "消费"), |
| | | APPLY_REFUND((byte)4, "申请退款"), |
| | | AUDIT_REFUND((byte)5, "退款审核"); |
| | | AUDIT_REFUND((byte)5, "退款审核"), |
| | | REFUND((byte)6, "退款"); |
| | | |
| | | private final Byte code; |
| | | private final String message; |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | |
| | | 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; |
| | |
| | | /** |
| | | * 申请退款API |
| | | */ |
| | | public static String refundUrl = "https://api.mch.weixin.qq.co/v3/refund/domestic/refunds"; |
| | | public static String refundUrl = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; |
| | | |
| | | /* |
| | | * 退款通知API |
| | |
| | | package com.dy.pipIrrSell.wechatpay; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | 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.util.RestTemplateUtil; |
| | | import com.dy.pipIrrSell.virtualCard.VirtualCardSv; |
| | | import com.dy.pipIrrSell.virtualCard.dto.DtoVirtualCard; |
| | | import com.dy.pipIrrSell.wechatpay.dto.*; |
| | | 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; |
| | |
| | | private final VirtualCardSv virtualCardSv; |
| | | private final ClientSv clientSv; |
| | | |
| | | private String privateCertFileName = PayInfo.privateCertFileName; |
| | | private String appid = PayInfo.appid; |
| | | private String mchid = PayInfo.mchid; |
| | | private String schema = PayInfo.schema; |
| | | private String signType = PayInfo.signType; |
| | | private String description = PayInfo.description; |
| | | private String loginUrl = PayInfo.loginUrl; |
| | | private String notifyUrl = PayInfo.notifyUrl; |
| | | private String grantType = PayInfo.grantType; |
| | | private String refundUrl = PayInfo.refundUrl; |
| | | 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 Map CERTIFICATE_MAP = new HashMap(); |
| | | private final Map CERTIFICATE_MAP = new HashMap(); |
| | | |
| | | /** |
| | | * 登录凭证校验 |
| | | * @param appid 小程序 appId |
| | | * @param secret 小程序 appSecret |
| | | * @param js_code 临时登录凭证code |
| | | * @param code2Session 登录凭证校验传入对象 |
| | | * @param bindingResult |
| | | * @return |
| | | * @throws Exception |
| | | */ |
| | |
| | | @PostMapping(path = "getSessionId") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public BaseResponse<Boolean> getSessionId(@RequestParam("appid") String appid, @RequestParam("secret") String secret, @RequestParam("js_code") String js_code) throws Exception { |
| | | 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", secret); |
| | | queryParams.put("js_code", js_code); |
| | | 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); |
| | |
| | | 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 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"); |
| | | } |
| | | //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(); |
| | |
| | | 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"); |
| | | } |
| | | // 暂时注释掉,认证通过后再放开 |
| | | //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 po 退款请求对象 |
| | | * @param bindingResult |
| | | * @return |
| | | * @throws NoSuchPaddingException |
| | | * @throws NoSuchAlgorithmException |
| | | * @throws InvalidKeySpecException |
| | | * @throws IOException |
| | | * @throws SignatureException |
| | | * @throws InvalidKeyException |
| | | */ |
| | | @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 = "refunds") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public BaseResponse<Boolean> refunds(@RequestBody @Valid Refund po, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException { |
| | | if(bindingResult != null && bindingResult.hasErrors()){ |
| | | return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 1. 判断交易时间是否超过一年 |
| | | * 2. 判断退款总金额是否超过订单金额 |
| | | * 3. 判断当前订单退款次数是否超过50次 |
| | | * 4. 判断与该订单上次退款是否相隔1分钟 |
| | | */ |
| | | |
| | | String tradeNo = po.getTradeNo(); |
| | | String refundNo = po.getRefundNo(); |
| | | Integer refund = po.getRefund(); |
| | | if(refundNo == null || refundNo.length() <= 0) { |
| | | // 新提退款申请,生成退款单号 |
| | | //refundNo = generateRefundNo(tradeNo); |
| | | } |
| | | |
| | | // 根据订单号获取总支付金额和总退款金额 |
| | | Integer totalTradeAmount = 0; |
| | | Integer totalRefundAmount = 0; |
| | | //Integer totalTradeAmount = getTotalTradeAmount(tradeNo); |
| | | //Integer totalRefundAmount = getTotalRefundAmount(tradeNo); |
| | | if(totalRefundAmount > totalTradeAmount) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.TOTAL_REFUND_EXCEED_TRADE.getMessage()); |
| | | } |
| | | |
| | | // 生成body |
| | | RefundRequest.Amount amount = new RefundRequest.Amount(); |
| | | amount.setRefund(refund); |
| | | amount.setTotal(totalTradeAmount); |
| | | 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 = payHelper.generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String method = "POST"; |
| | | String httpUrl = "/v3/refund/domestic/refunds"; |
| | | |
| | | String body = JSONObject.toJSONString(refundRequest); |
| | | 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_refundResponse = restTemplateUtil.post(PayInfo.orderUrl, 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()); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | } 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); |
| | | } |
| | | } |
| | | |
| | | // 通知应答 |
New file |
| | |
| | | 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; |
| | | } |
| | |
| | | private String tradeNo; |
| | | |
| | | /** |
| | | * 退款单号,退款单号无值:新提交退款申请。退款单号有值:重新提交退款 |
| | | * 退款单号 |
| | | */ |
| | | @Schema(description = "退款单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private String refundNo; |
New file |
| | |
| | | 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; |
| | | } |