微信支付退款

This commit is contained in:
haown 2025-08-25 10:37:32 +08:00
parent cee381fcef
commit f92a088959
33 changed files with 908 additions and 82 deletions

View File

@ -1,15 +1,15 @@
package com.yf.exam;
import java.net.InetAddress;
import java.net.UnknownHostException;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 云帆在线考试系统
* @author bool
@ -18,6 +18,7 @@ import java.net.UnknownHostException;
*/
@Log4j2
@SpringBootApplication
@EnableScheduling
public class ExamApplication implements WebMvcConfigurer {
public static void main(String[] args) throws UnknownHostException {

View File

@ -52,6 +52,7 @@ public class ShiroConfig {
// 小程序接口
map.put("/examApplet/**", "anon");
map.put("/exam/weChatPayment/**", "anon");
// 获取网站基本信息
map.put("/exam/api/sys/config/detail", "anon");

View File

@ -27,14 +27,15 @@ public class AppletExamController extends BaseController {
private ExamService examService;
/**
* 查询可考试列表
* 查询可考试列表排除了已经报名的考试
* @param reqDTO
* @return
*/
@ApiOperation(value = "查询可考试列表")
@ApiOperation(value = "查询可考试列表(排除了已经报名的考试)")
@RequestMapping(value = "/getExamList", method = { RequestMethod.GET})
public ApiRest getExamList(ExamSearchDTO reqDTO) {
List<ExamDTO> list = examService.getExamList(reqDTO);
return super.success(list);
}
}

View File

@ -1,15 +1,20 @@
package com.yf.exam.modules.applet.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.enums.GooodsOrderStatusEnum;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.modules.exam.dto.ExamRegistrationDTO;
import com.yf.exam.modules.exam.dto.response.ExamRegistrationVO;
import com.yf.exam.modules.exam.entity.ExamRegistration;
import com.yf.exam.modules.exam.service.ExamRegistrationService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@ -42,14 +47,40 @@ public class AppletExamRegistrationController extends BaseController {
/**
* 查询已报名的考试列表
* 查询已报名的正式考试列表
* @return
*/
@ApiOperation(value = "查询已报名的考试列表")
@ApiOperation(value = "查询已报名的正式考试列表")
@RequestMapping(value = "/getRegExamList", method = { RequestMethod.GET})
public ApiRest getRegExamList(ExamRegistrationDTO examRegistrationDTO) {
examRegistrationDTO.setPaymentState(GooodsOrderStatusEnum.PAY.getInfo());
List<ExamRegistrationVO> list = baseService.getRegExamList(examRegistrationDTO);
return super.success(list);
}
/**
* 模拟考试列表查询所有的模拟考试返回是否已购买标识
* @return
*/
@ApiOperation(value = "模拟考试列表,查询所有的模拟考试,返回是否已购买标识")
@RequestMapping(value = "/getMockExamList", method = { RequestMethod.POST})
public ApiRest getMockExamList(@RequestBody PagingReqDTO<ExamRegistrationDTO> reqDTO) {
//分页查询并转换
IPage<ExamRegistrationVO> page = baseService.getMockExamList(reqDTO);
return super.success(page);
}
/**
* 小程序点击退款修改订单
*/
@ApiOperation(value = "小程序点击退款修改订单")
@PostMapping("/updateRegRefound")
public AjaxResult updateRegRefound(@RequestBody ExamRegistration examRegistration) {
if (Objects.isNull(examRegistration.getId())) {
return AjaxResult.error("订单id不能为空");
}
if (Objects.isNull(examRegistration.getRefundReason())) {
return AjaxResult.error("请填写退款原因!");
}
return toAjax(baseService.updateRegRefound(examRegistration));
}
}

View File

@ -3,11 +3,11 @@ package com.yf.exam.modules.applet.controller;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.modules.applet.dto.PaymentDTO;
import com.yf.exam.modules.applet.dto.RefundDTO;
import com.yf.exam.modules.applet.service.WeChatPayNotifyService;
import com.yf.exam.modules.applet.service.WeChatPaymentService;
import com.yf.exam.modules.applet.service.WeChatRefundService;
import com.yf.exam.modules.payment.dto.PaymentDTO;
import com.yf.exam.modules.payment.dto.RefundDTO;
import com.yf.exam.modules.payment.service.WeChatPayNotifyService;
import com.yf.exam.modules.payment.service.WeChatPaymentService;
import com.yf.exam.modules.payment.service.WeChatRefundService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.math.BigDecimal;
@ -32,7 +32,7 @@ import org.springframework.web.bind.annotation.RestController;
@Api(tags={"支付控制器"})
@RestController
@RequestMapping("/examApplet/weChatPayment")
public class WeChatPaymentController extends BaseController {
public class AppletWeChatPaymentController extends BaseController {
@Resource
private WeChatPaymentService weChatPaymentService;

View File

@ -5,6 +5,7 @@ import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.exam.dto.ExamRegistrationDTO;
import com.yf.exam.modules.exam.dto.ExamRegistrationUpdDTO;
import com.yf.exam.modules.exam.dto.response.ExamRegistrationVO;
import com.yf.exam.modules.exam.service.ExamRegistrationService;
import io.swagger.annotations.Api;
@ -48,11 +49,22 @@ public class ExamRegistrationController extends BaseController {
*/
@ApiOperation(value = "查询已报名的考试列表")
@RequestMapping(value = "/getRegExamList", method = {RequestMethod.GET})
public ApiRest getExamList(ExamRegistrationDTO examRegistrationDTO) {
public ApiRest getRegExamList(ExamRegistrationDTO examRegistrationDTO) {
List<ExamRegistrationVO> list = baseService.getRegExamList(examRegistrationDTO);
return super.success(list);
}
/**
* 分页查询已报名的考试列表
* @return
*/
@ApiOperation(value = "分页查询已报名的考试列表")
@RequestMapping(value = "/getRegExamPage", method = {RequestMethod.POST})
public ApiRest<IPage<ExamRegistrationVO>> getRegExamPage(@RequestBody PagingReqDTO<ExamRegistrationDTO> reqDTO) {
IPage<ExamRegistrationVO> page = baseService.getRegExamPage(reqDTO);
return super.success(page);
}
/**
* 分页查询考生列表
* @return
@ -64,4 +76,15 @@ public class ExamRegistrationController extends BaseController {
IPage<ExamRegistrationVO> page = baseService.getRegUserList(reqDTO);
return super.success(page);
}
/**
* 修改考试完成状态
* @param reqDTO
* @return
*/
@ApiOperation(value = "修改考试完成状态")
@RequestMapping(value = "/updFinishState", method = { RequestMethod.POST})
public ApiRest updFinishState(@RequestBody ExamRegistrationUpdDTO reqDTO) {
return super.success(baseService.updateFinishStateById(reqDTO.getFinishState(), reqDTO.getId()));
}
}

View File

@ -85,9 +85,9 @@ public class ExamRegistrationDTO extends UserAttachment implements Serializable
private Integer finishState;
/**
* 交费状态待付款WAIT_PAY已付款PAY已取消CANCEL待收货WAIT_RECEIVED_GOODS已收货RECEIVED_GOODS退款中WAIT_REFUND已退款REFUNDED待退货WAIT_RETURNED_GOODS已退货RETURNED_GOODS
* 交费状态待付款WAIT_PAY已付款PAY已取消CANCEL退款中WAIT_REFUND已退款REFUNDED
*/
@ApiModelProperty(value = "交费状态待付款WAIT_PAY已付款PAY已取消CANCEL待收货WAIT_RECEIVED_GOODS已收货RECEIVED_GOODS退款中WAIT_REFUND已退款REFUNDED待退货WAIT_RETURNED_GOODS已退货RETURNED_GOODS", required=true)
@ApiModelProperty(value = "交费状态待付款WAIT_PAY已付款PAY已取消CANCEL退款中WAIT_REFUND已退款REFUNDED", required=true)
private String paymentState;
/**
@ -112,4 +112,16 @@ public class ExamRegistrationDTO extends UserAttachment implements Serializable
* */
@ApiModelProperty(value = "交费结束时间", required=true)
private Date paymentEndDate;
/**
* 考试类型多种类型查询用,拼接
* */
@ApiModelProperty(value = "考试类型1模拟考试2正式考试3补考", required=true)
private String examTypes;
/**
* 交费状态待付款WAIT_PAY已付款PAY已取消CANCEL退款中WAIT_REFUND已退款REFUNDED多种类型查询用,拼接
* */
@ApiModelProperty(value = "交费状态待付款WAIT_PAY已付款PAY已取消CANCEL退款中WAIT_REFUND已退款REFUNDED多种类型查询用,拼接)", required=true)
private String paymentStates;
}

View File

@ -0,0 +1,28 @@
package com.yf.exam.modules.exam.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @description: 修改状态传输对象
* @author: haown
* @create: 2025-08-18 16:17
**/
@Data
@ApiModel(value="修改状态传输对象", description="修改状态传输对象")
public class ExamRegistrationUpdDTO {
/**
* ID
*/
@ApiModelProperty(value = "ID", required=true)
private String id;
/**
* 考试状态0未考试1已考试
*/
@ApiModelProperty(value = "考试状态0未考试1已考试", required=true)
private Integer finishState;
}

View File

@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Date;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
@ -16,6 +17,11 @@ import org.springframework.format.annotation.DateTimeFormat;
@Data
public class ExamRegistrationVO {
/**
* 报名表主键
*/
private String id;
/**
* 用户主键
*/
@ -54,11 +60,17 @@ public class ExamRegistrationVO {
private Integer finishState;
/**
* 交费状态待付款WAIT_PAY已付款PAY已取消CANCEL待收货WAIT_RECEIVED_GOODS已收货RECEIVED_GOODS退款中WAIT_REFUND已退款REFUNDED待退货WAIT_RETURNED_GOODS已退货RETURNED_GOODS
* 交费状态待付款WAIT_PAY已付款PAY已取消CANCEL退款中WAIT_REFUND已退款REFUNDED
*/
@ApiModelProperty(value = "交费状态待付款WAIT_PAY已付款PAY已取消CANCEL待收货WAIT_RECEIVED_GOODS已收货RECEIVED_GOODS退款中WAIT_REFUND已退款REFUNDED待退货WAIT_RETURNED_GOODS已退货RETURNED_GOODS", required=true)
@ApiModelProperty(value = "交费状态待付款WAIT_PAY已付款PAY已取消CANCEL退款中WAIT_REFUND已退款REFUNDED", required=true)
private String paymentState;
/**
* 交费时间
*/
@ApiModelProperty(value = "交费时间", required=true)
private Date paymentDate;
@ApiModelProperty(value = "考试主键", required=true)
private String examId;
@ -114,4 +126,28 @@ public class ExamRegistrationVO {
@ApiModelProperty(value = "考试费用", required=true)
private BigDecimal examFee;
/**
* 确认退款状态未确认NOT_CONFIRM已确认CONFIRMED
*/
@ApiModelProperty(value = "确认退款状态未确认NOT_CONFIRM已确认CONFIRMED", required=true)
private String confirmRefundStatus;
/**
* 退款原因
*/
@ApiModelProperty(value = "退款原因")
private String refundReason;
/**
* 模拟考试有效期
*/
@ApiModelProperty(value = "模拟考试有效期", required=true)
private BigDecimal expirationDate;
@ApiModelProperty(value = "天数是否允许退款true允许false不允许", required=true)
private Boolean daysAllow;
@ApiModelProperty(value = "购买状态true已购买false未购买", required=true)
private Boolean buyState;
}

View File

@ -106,6 +106,12 @@ public class ExamRegistration extends UserAttachment {
*/
private String confirmRefundStatus;
/**
* 退款原因
*/
@ApiModelProperty(value = "退款原因")
private String refundReason;
/**
* 创建时间
*/

View File

@ -19,11 +19,35 @@ import org.apache.ibatis.annotations.Param;
*/
public interface ExamRegistrationMapper extends BaseMapper<ExamRegistration> {
List<ExamRegistrationVO> getRegExamList(ExamRegistrationDTO examRegistrationDTO);
List<ExamRegistrationVO> getRegExamList(@Param("query") ExamRegistrationDTO examRegistrationDTO);
IPage<ExamRegistrationVO> getRegExamPage(Page page, @Param("query") ExamRegistrationDTO examRegistrationDTO);
void updateFinishState(ExamRegistration examRegistration);
IPage<ExamRegistrationVO> getRegUserList(Page page, @Param("query") ExamRegistrationDTO examRegistrationDTO);
int updatePaymentStateById(String paymentState, String id);
int updateFinishStateById(Integer finishState, String id);
IPage<ExamRegistrationVO> getMockExamList(Page page, @Param("query") ExamRegistrationDTO examRegistrationDTO);
/**
* 根据订单编号修改商品订单的确认退款状态
*
* @param confirmRefundStatus 确认退款状态
* @param id 订单编号
* @return 数量
*/
int updateConfirmRefundStatus(@Param("id") String id, @Param("confirmRefundStatus") String confirmRefundStatus);
/**
* 批量更新商品订单状态
*
* @param ids 订单列表信息
* @param paymentState 订单状态
* @return 更新数量
*/
int updateBatchPaymentState(@Param("ids") List<String> ids, @Param("paymentState") String paymentState);
}

View File

@ -33,6 +33,15 @@ public interface ExamRegistrationService extends IService<ExamRegistration> {
*/
List<ExamRegistrationVO> getRegExamList(ExamRegistrationDTO examRegistrationDTO);
/**
* @description 分页查询已报名的考试列表
* @Param
* @return null
* @Author haown
* @Date 2025-8-21 16:27
*/
IPage<ExamRegistrationVO> getRegExamPage(PagingReqDTO<ExamRegistrationDTO> reqDTO);
void updateFinishState(ExamRegistration examRegistration);
/**
@ -44,4 +53,16 @@ public interface ExamRegistrationService extends IService<ExamRegistration> {
*/
IPage<ExamRegistrationVO> getRegUserList(PagingReqDTO<ExamRegistrationDTO> examRegistrationDTO);
int updateFinishStateById(Integer finishState, String id);
IPage<ExamRegistrationVO> getMockExamList(PagingReqDTO<ExamRegistrationDTO> examRegistrationDTO);
/**
* @description 修改报名状态退款
* @Param
* @Author haown
* @Date 2025-8-20 15:43
*/
int updateRegRefound(ExamRegistration examRegistration);
}

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.core.enums.ConfirmRefundStatusEnum;
import com.yf.exam.core.enums.GooodsOrderStatusEnum;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.modules.exam.dto.ExamRegistrationDTO;
@ -14,6 +15,9 @@ import com.yf.exam.modules.exam.entity.ExamRegistration;
import com.yf.exam.modules.exam.mapper.ExamRegistrationMapper;
import com.yf.exam.modules.exam.service.ExamRegistrationService;
import com.yf.exam.modules.paper.enums.ExamFinishState;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@ -29,12 +33,13 @@ public class ExamRegistrationServiceImpl extends ServiceImpl<ExamRegistrationMap
@Override
public String save(ExamRegistrationDTO reqDTO) {
if (StringUtils.isBlank(reqDTO.getUserId())) {
if (StringUtils.isBlank(reqDTO.getUserId()) || StringUtils.isBlank(reqDTO.getUserName()) || StringUtils.isBlank(reqDTO.getRealName()) || StringUtils.isBlank(reqDTO.getPhone())) {
throw new ServiceException("传输数据错误,请重新报名");
}
if (StringUtils.isBlank(reqDTO.getExamId())) {
if (StringUtils.isBlank(reqDTO.getExamId()) || StringUtils.isBlank(reqDTO.getTitle()) || reqDTO.getExamType() == null) {
throw new ServiceException("请选择要报名的考试");
}
// 查询考生是否报名过该考试
ExamRegistrationDTO examRegistrationDTO = new ExamRegistrationDTO();
examRegistrationDTO.setExamId(reqDTO.getExamId());
@ -57,8 +62,12 @@ public class ExamRegistrationServiceImpl extends ServiceImpl<ExamRegistrationMap
// 复制基本数据
BeanUtils.copyProperties(reqDTO, entity);
entity.setId(id);
entity.setOrderTime(LocalDateTime.now());
entity.setPaymentState(GooodsOrderStatusEnum.WAIT_PAY.getInfo());
entity.setFinishState(ExamFinishState.UNFINISH);
if (reqDTO.getExamType() == 1) {
entity.setRegTime(LocalDate.now());
}
this.saveOrUpdate(entity);
return id;
}
@ -68,6 +77,16 @@ public class ExamRegistrationServiceImpl extends ServiceImpl<ExamRegistrationMap
return baseMapper.getRegExamList(examRegistrationDTO);
}
@Override public IPage<ExamRegistrationVO> getRegExamPage(PagingReqDTO<ExamRegistrationDTO> reqDTO) {
// 创建分页对象
Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize());
// 查找分页
IPage<ExamRegistrationVO> pageData = baseMapper.getRegExamPage(page, reqDTO.getParams());
return pageData;
}
@Override public void updateFinishState(ExamRegistration examRegistration) {
baseMapper.updateFinishState(examRegistration);
}
@ -81,4 +100,29 @@ public class ExamRegistrationServiceImpl extends ServiceImpl<ExamRegistrationMap
return pageData;
}
@Override public int updateFinishStateById(Integer finishState, String id) {
return baseMapper.updateFinishStateById(finishState, id);
}
@Override public IPage<ExamRegistrationVO> getMockExamList(PagingReqDTO<ExamRegistrationDTO> reqDTO) {
// 创建分页对象
Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize());
// 查找分页
IPage<ExamRegistrationVO> pageData = baseMapper.getMockExamList(page, reqDTO.getParams());
return pageData;
}
@Override
public int updateRegRefound(ExamRegistration examRegistration) {
//设置修改人以及修改时间
examRegistration.setUpdateTime(new Date());
//订单状态 退款中 WAIT_REFUND
examRegistration.setPaymentState(GooodsOrderStatusEnum.WAIT_REFUND.getInfo());
//确认退款状态 未确认 NOT_CONFIRM
examRegistration.setConfirmRefundStatus(ConfirmRefundStatusEnum.NOT_CONFIRM.getInfo());
return baseMapper.updateById(examRegistration);
}
}

View File

@ -0,0 +1,95 @@
package com.yf.exam.modules.payment.controller;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.modules.payment.dto.RefundDTO;
import com.yf.exam.modules.payment.service.WeChatPayNotifyService;
import com.yf.exam.modules.payment.service.WeChatPaymentService;
import com.yf.exam.modules.payment.service.WeChatRefundService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description 微信小程序支付控制器
* @Author 纪寒
* @Date 2022-10-18 14:47:11
* @Version 1.0`
*/
@Api(tags={"PC端支付控制器"})
@RestController
@RequestMapping("/exam/weChatPayment")
public class WeChatPaymentController extends BaseController {
@Resource
private WeChatPaymentService weChatPaymentService;
@Resource
private WeChatPayNotifyService weChatPayNotifyService;
@Resource
private WeChatRefundService weChatRefundService;
/**
* 取消订单接口暂时废弃
*
* @param orderNo 订单编号
* @return 返回结果信息
* @throws Exception 异常信息
*/
@PostMapping("/cancelOrderInfo")
public AjaxResult cancelOrderInfo(@RequestParam("orderNo") String orderNo) throws Exception {
if (StringUtils.isBlank(orderNo)) {
return AjaxResult.error("请选择所要取消的订单信息!");
}
return weChatPaymentService.cancelOrderInfo(orderNo);
}
/**
* 查询订单状态接口暂时废弃
*
* @param orderNo 订单编号
* @return 返回结果信息
* @throws Exception 异常信息
*/
@PostMapping("/getOrderStatusInfo")
public AjaxResult getOrderStatusInfo(@RequestParam("orderNo") String orderNo) throws Exception {
if (StringUtils.isBlank(orderNo)) {
return AjaxResult.error("请选择所要查询的订单信息!");
}
return weChatPaymentService.getOrderStatusInfo(orderNo);
}
/**
* 微信确认退款接口
*
* @param refundDTO 退款参数
* @return 退款申请结果
* @throws Exception 异常信息
*/
@ApiOperation(value = "微信确认退款接口")
@PostMapping("/weChatRefundOrderApply")
public AjaxResult weChatRefundOrderApply(@RequestBody RefundDTO refundDTO) throws Exception {
return weChatRefundService.weChatRefundOrderApply(refundDTO);
}
/**
* 新医路微信退款回调通知接口
*
* @param request 请求头信息
* @param response 响应信息
* @return 应答信息避免微信平台重复发送回调通知
* @throws Exception 异常信息
*/
@PostMapping("/xylWeChatRefundNotify")
public String xylWeChatRefundNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
return weChatPayNotifyService.xylWeChatRefundNotify(request, response);
}
}

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.dto;
package com.yf.exam.modules.payment.dto;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.dto;
package com.yf.exam.modules.payment.dto;
import java.io.Serializable;
import java.math.BigDecimal;

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.service;
package com.yf.exam.modules.payment.service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

View File

@ -1,8 +1,8 @@
package com.yf.exam.modules.applet.service;
package com.yf.exam.modules.payment.service;
import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.modules.applet.dto.PaymentDTO;
import com.yf.exam.modules.applet.vo.OrderStatusInfoVO;
import com.yf.exam.modules.payment.dto.PaymentDTO;
import com.yf.exam.modules.payment.vo.OrderStatusInfoVO;
/**
* @Description 微信小程序和App支付业务层

View File

@ -1,7 +1,7 @@
package com.yf.exam.modules.applet.service;
package com.yf.exam.modules.payment.service;
import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.modules.applet.dto.RefundDTO;
import com.yf.exam.modules.payment.dto.RefundDTO;
/**
* @Description 微信退款业务层
@ -11,6 +11,13 @@ import com.yf.exam.modules.applet.dto.RefundDTO;
*/
public interface WeChatRefundService {
/**
* 根据订单id判断是否符合退款条件
* @param refundDTO 退款参数
* @return 返回信息
*/
AjaxResult judgeRefund(RefundDTO refundDTO);
/**
* 微信确认退款接口
*

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.service.impl;
package com.yf.exam.modules.payment.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@ -16,8 +16,8 @@ import com.yf.exam.core.enums.WeChatTradeStateEnum;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.core.utils.RedisDistributedLockUtils;
import com.yf.exam.core.utils.http.HttpUtils;
import com.yf.exam.modules.applet.service.WeChatPayNotifyService;
import com.yf.exam.modules.applet.vo.WeChatPayNotifyPlaintextVO;
import com.yf.exam.modules.payment.service.WeChatPayNotifyService;
import com.yf.exam.modules.payment.vo.WeChatPayNotifyPlaintextVO;
import com.yf.exam.modules.exam.entity.ExamRegistration;
import com.yf.exam.modules.exam.mapper.ExamRegistrationMapper;
import com.yf.exam.modules.payment.entity.PaymentInfo;
@ -219,7 +219,9 @@ public class WeChatPayNotifyServiceImpl implements WeChatPayNotifyService {
if (Objects.nonNull(examRegistration)) {
insertPaymentInfo(notifyPlaintext, examRegistration, plainText);
if (WeChatTradeStateEnum.SUCCESS.getInfo().equals(notifyPlaintext.getTradeState())) {
examRegistrationMapper.updatePaymentStateById(GooodsOrderStatusEnum.PAY.getInfo(), examRegistration.getId());
examRegistration.setPaymentState(GooodsOrderStatusEnum.PAY.getInfo());
examRegistration.setPaymentDate(new Date());
examRegistrationMapper.updateById(examRegistration);
} else {
log.info("商品订单微信订单状态异常,订单状态为 ====> {}", notifyPlaintext.getTradeState());
}
@ -343,7 +345,7 @@ public class WeChatPayNotifyServiceImpl implements WeChatPayNotifyService {
if (RefundStatusEnum.SUCCESS.getInfo().equals(refundNotifyVO.getRefundStatus())
&& StringUtils.isNotBlank(examRegistration.getPaymentState())
&& !(GooodsOrderStatusEnum.REFUNDED.getInfo().equals(examRegistration.getPaymentState()))) {
int updateCount = examRegistrationMapper.updatePaymentStateById(GooodsOrderStatusEnum.REFUNDED.getInfo(), examRegistration.getPaymentState());
int updateCount = examRegistrationMapper.updatePaymentStateById(GooodsOrderStatusEnum.REFUNDED.getInfo(), examRegistration.getId());
if (updateCount < 1) {
log.info("微信退款通知,修改商品单状态失败,订单编号 =====> {}", refundNotifyVO.getOutTradeNo());
}

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.service.impl;
package com.yf.exam.modules.payment.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@ -10,11 +10,11 @@ import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.core.enums.GooodsOrderStatusEnum;
import com.yf.exam.core.enums.WeChatTradeStateEnum;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.modules.applet.dto.PaymentDTO;
import com.yf.exam.modules.applet.service.WeChatPaymentService;
import com.yf.exam.modules.applet.vo.OrderStatusInfoVO;
import com.yf.exam.modules.applet.vo.WeChatAppletSignVO;
import com.yf.exam.modules.applet.vo.WeChatQueryOrderVO;
import com.yf.exam.modules.payment.dto.PaymentDTO;
import com.yf.exam.modules.payment.service.WeChatPaymentService;
import com.yf.exam.modules.payment.vo.OrderStatusInfoVO;
import com.yf.exam.modules.payment.vo.WeChatAppletSignVO;
import com.yf.exam.modules.payment.vo.WeChatQueryOrderVO;
import com.yf.exam.modules.exam.entity.ExamRegistration;
import com.yf.exam.modules.exam.mapper.ExamRegistrationMapper;
import com.yf.exam.modules.utils.WeChatUtil;

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.service.impl;
package com.yf.exam.modules.payment.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
@ -6,18 +6,22 @@ import com.yf.exam.config.WeChatPaymentUrlConfig;
import com.yf.exam.config.XylWeChatPaymentConfig;
import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.core.enums.ConfirmRefundStatusEnum;
import com.yf.exam.core.enums.GooodsOrderStatusEnum;
import com.yf.exam.core.enums.RefundStatusEnum;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.modules.applet.dto.RefundDTO;
import com.yf.exam.modules.applet.service.WeChatRefundService;
import com.yf.exam.modules.exam.entity.Exam;
import com.yf.exam.modules.exam.entity.ExamRegistration;
import com.yf.exam.modules.exam.mapper.ExamMapper;
import com.yf.exam.modules.exam.mapper.ExamRegistrationMapper;
import com.yf.exam.modules.payment.dto.RefundDTO;
import com.yf.exam.modules.payment.entity.RefundInfo;
import com.yf.exam.modules.payment.mapper.RefundInfoMapper;
import com.yf.exam.modules.payment.service.WeChatRefundService;
import com.yf.exam.modules.payment.vo.WeChatRefundInfoVO;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
@ -54,6 +58,8 @@ public class WeChatRefundServiceImpl implements WeChatRefundService {
private WeChatPaymentUrlConfig weChatPaymentUrlConfig;
@Resource
private RefundInfoMapper refundInfoMapper;
@Resource
private ExamMapper examMapper;
/*@Resource
private GoodsStockService goodsStockService;
@Resource
@ -75,9 +81,34 @@ public class WeChatRefundServiceImpl implements WeChatRefundService {
/**
* 新医路退款回调接口地址
*/
private static final String XINYILU_WE_CHAT_REFUND_URL = "/examApplet/weChatPayment/xylWeChatRefundNotify";
private static final String XINYILU_WE_CHAT_REFUND_URL = "/exam/weChatPayment/xylWeChatRefundNotify";
/**
@Override public AjaxResult judgeRefund(RefundDTO refundDTO) {
if (StringUtils.isBlank(refundDTO.getId())) {
return AjaxResult.error("数据错误!");
}
ExamRegistration examRegistration = examRegistrationMapper.selectById(refundDTO.getId());
if (Objects.nonNull(examRegistration)) {
AjaxResult ajaxResult = judgeRefundInfo(examRegistration.getOrderTime(), refundDTO.getRefundPrice(), examRegistration.getExamFee());
if (Objects.nonNull(ajaxResult)) {
return ajaxResult;
}
// 判断是否已考试
if (examRegistration.getFinishState() != null && examRegistration.getFinishState() == 1) {
return AjaxResult.error("您已考试,不能进行退款!");
}
// 正式考试考试前两天不允许退款
Exam exam = examMapper.selectById(examRegistration.getExamId());
if (Objects.nonNull(exam) && (exam.getExamType() == 2 || exam.getExamType() == 3)) {
if (LocalDate.now().plusDays(2).isAfter(exam.getStartDate()) || LocalDate.now().plusDays(2).isEqual(exam.getStartDate())) {
return AjaxResult.error("请在考试开始时间前两天退款,逾期不予退款!");
}
}
}
return AjaxResult.success();
}
/**
* 微信确认退款接口
*
* @param refundDTO 退款参数
@ -87,23 +118,34 @@ public class WeChatRefundServiceImpl implements WeChatRefundService {
@Transactional(rollbackFor = Exception.class)
@Override
public AjaxResult weChatRefundOrderApply(RefundDTO refundDTO) throws Exception {
//取消商品订单信息修改本地订单状态并调用微信退款申请接口进行退款处理
// 取消商品订单信息修改本地订单状态并调用微信退款申请接口进行退款处理
ExamRegistration examRegistration = examRegistrationMapper.selectById(refundDTO.getId());
if (Objects.nonNull(examRegistration)) {
AjaxResult ajaxResult = judgeRefundInfo(examRegistration.getOrderTime(), refundDTO.getRefundPrice(), examRegistration.getExamFee());
if (Objects.nonNull(ajaxResult)) {
return ajaxResult;
}
// 判断是否已考试
if (examRegistration.getFinishState() != null && examRegistration.getFinishState() == 1) {
return AjaxResult.error("已考试,不能进行退款!");
}
// 正式考试考试前两天不允许退款
Exam exam = examMapper.selectById(examRegistration.getExamId());
if (Objects.nonNull(exam) && (exam.getExamType() == 2 || exam.getExamType() == 3)) {
if(LocalDate.now().plusDays(2).isAfter(exam.getStartDate()) || LocalDate.now().plusDays(2).isEqual(exam.getStartDate())) {
return AjaxResult.error("考试前两天不允许退款!");
}
}
//判断订单状态是否确认退款
if (StringUtils.isBlank(examRegistration.getConfirmRefundStatus())
/*if (StringUtils.isBlank(examRegistration.getConfirmRefundStatus())
|| !(ConfirmRefundStatusEnum.NOT_CONFIRM.getInfo().equals(examRegistration.getConfirmRefundStatus()))) {
return AjaxResult.error("当前商品订单未进行退款确认,请先进行确认!");
}
/*if (StringUtils.isBlank(examRegistration.getPaymentState())
}*/
if (StringUtils.isBlank(examRegistration.getPaymentState())
|| !GooodsOrderStatusEnum.WAIT_REFUND.getInfo().equals(examRegistration.getPaymentState())
|| GooodsOrderStatusEnum.REFUNDED.getInfo().equals(examRegistration.getPaymentState())) {
return AjaxResult.error("当前商品订单非退款中或者已退款,无法进行退款处理!");
}*/
}
//调用微信申请退款接口发起退款申请
String userId = Objects.isNull(examRegistration.getUserId()) ? "" : examRegistration.getUserId();
String outRefundNo = fillZeroByUserId(userId, 5) + System.nanoTime();
@ -218,10 +260,10 @@ public class WeChatRefundServiceImpl implements WeChatRefundService {
* @return 返回结果信息
*/
private AjaxResult judgeRefundInfo(LocalDateTime orderTime, BigDecimal refundPrice, BigDecimal totalPrice) {
/*LocalDateTime localDateTime = orderTime.plusYears(1);
LocalDateTime localDateTime = orderTime.plusYears(1);
if (LocalDateTime.now().isAfter(localDateTime)) {
return AjaxResult.error("当前订单超过一年,无法进行退款处理!");
}*/
}
if (refundPrice.compareTo(totalPrice) > 0) {
return AjaxResult.error("当前退款金额大于订单支付金额,无法进行退款处理!");
}
@ -293,11 +335,11 @@ public class WeChatRefundServiceImpl implements WeChatRefundService {
throw new ServiceException("微信申请退款异常, 响应码:" + statusCode + ", 退款返回结果:" + body);
}
//将商品订单状态修改为已确认
/*if (Objects.nonNull(goodsOrderInfo)) {
examRegistrationMapper.updateGoodsConfirmRefundStatus(goodsOrderInfo.getOrderNo(), ConfirmRefundStatusEnum.CONFIRMED.getInfo());
}*/
if (Objects.nonNull(goodsOrderInfo)) {
examRegistrationMapper.updateConfirmRefundStatus(goodsOrderInfo.getId(), ConfirmRefundStatusEnum.CONFIRMED.getInfo());
}
WeChatRefundInfoVO weChatRefundInfoVO = JSONObject.parseObject(body, WeChatRefundInfoVO.class);
//记录支付日志
// 记录退款日志
int refundInfoCount = refundInfoMapper.getRefundInfoByexamRegistrationId(weChatRefundInfoVO.getOutTradeNo());
if (refundInfoCount <= 0) {
RefundInfo refundInfo = buildRefundInfo(userId, weChatRefundInfoVO, refundDTO, body, refundMerchantType);
@ -344,9 +386,9 @@ public class WeChatRefundServiceImpl implements WeChatRefundService {
refundInfo.setApplyRefundReturnContent(refundBody);
refundInfo.setRefundMerchantType(refundMerchantType);
refundInfo.setDelFlag(0);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
refundInfo.setCreateTime(formatter.parse(refundInfoVO.getCreateTime()));
refundInfo.setCreateTime(sdf.parse(refundInfoVO.getCreateTime()));
} catch (ParseException e) {
throw new RuntimeException(e);
}

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.vo;
package com.yf.exam.modules.payment.vo;
import java.io.Serializable;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.vo;
package com.yf.exam.modules.payment.vo;
import java.io.Serializable;
import lombok.Builder;

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.vo;
package com.yf.exam.modules.payment.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package com.yf.exam.modules.applet.vo;
package com.yf.exam.modules.payment.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;

View File

@ -0,0 +1,41 @@
package com.yf.exam.modules.quartz.controller;
import com.yf.exam.modules.quartz.task.PaymentInfoTask;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description 手动处理支付定时任务控制器
* @Author 纪寒
* @Date 2022-10-21 15:11:38
* @Version 1.0
*/
@RestController
@RequestMapping("/monitor/payTask")
public class PaymentInfoTaskController {
@Resource
private PaymentInfoTask paymentInfoTask;
/**
* 手动执行关闭订单定时任务
*
* @throws Exception 异常信息
*/
@GetMapping("/handCloseOrder")
public void handCloseOrder() throws Exception {
paymentInfoTask.automaticOrderTask();
}
/**
* 手动执行更新订单状态定时任务
*
* @throws Exception 异常信息
*/
@GetMapping("/handOrderStatus")
public void handOrderStatus() throws Exception {
paymentInfoTask.automaticUpdateOrderStatusTask();
}
}

View File

@ -0,0 +1,24 @@
package com.yf.exam.modules.quartz.service;
/**
* @Description 支付定时任务业务层
* @Author 纪寒
* @Date 2022-10-21 15:03:05
* @Version 1.0
*/
public interface PaymentInfoTaskService {
/**
* 自动关闭商品订单24小时与预约订单2小时未支付的订单信息每10分钟执行一次
*
* @throws Exception 异常信息
*/
void automaticOrderTask() throws Exception;
/**
* 自动修改未支付的订单状态每10分钟执行一次
*
* @throws Exception 异常信息
*/
void automaticUpdateOrderStatusTask() throws Exception;
}

View File

@ -0,0 +1,229 @@
package com.yf.exam.modules.quartz.service.impl;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.yf.exam.config.XylWeChatPaymentConfig;
import com.yf.exam.core.enums.GooodsOrderStatusEnum;
import com.yf.exam.core.enums.PayTypeEnum;
import com.yf.exam.core.enums.WeChatTradeStateEnum;
import com.yf.exam.modules.exam.entity.ExamRegistration;
import com.yf.exam.modules.exam.mapper.ExamRegistrationMapper;
import com.yf.exam.modules.payment.entity.PaymentInfo;
import com.yf.exam.modules.payment.mapper.PaymentInfoMapper;
import com.yf.exam.modules.payment.service.WeChatPaymentService;
import com.yf.exam.modules.payment.vo.OrderStatusInfoVO;
import com.yf.exam.modules.payment.vo.WeChatQueryOrderVO;
import com.yf.exam.modules.quartz.service.PaymentInfoTaskService;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import javax.annotation.Resource;
import javax.sql.rowset.serial.SerialException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
/**
* @Description 支付定时任务实现类
* @Author 纪寒
* @Date 2022-10-21 15:03:41
* @Version 1.0
*/
@Slf4j
@Service
public class PaymentInfoTaskServiceImpl implements PaymentInfoTaskService {
@Resource
private ExamRegistrationMapper examRegistrationMapper;
@Resource
private WeChatPaymentService weChatPaymentService;
@Resource
private PaymentInfoMapper paymentInfoMapper;
@Resource
private XylWeChatPaymentConfig xylWeChatPaymentConfig;
@Resource(name = "transactionManager")
private DataSourceTransactionManager transactionManager;
/**
* 成功状态码
*/
private static final int SUCCESS_CODE = 200;
/**
* 成功状态码
*/
private static final int SUCCESS_CODE_TWO = 204;
/**
* 自动关闭商品订单2小时未支付的订单信息每10分钟执行一次
*
* @throws Exception 异常信息
*/
@Override
public void automaticOrderTask() throws Exception {
//查询出当前系统中商品订单超过2小时前与预约订单超过2小时前未支付的订单列表信息
LocalDateTime localDateTime = LocalDateTime.now().minusHours(2);
//查询考试订单信息
QueryWrapper<ExamRegistration> wrapper = new QueryWrapper<>();
wrapper.lambda().le(ExamRegistration::getOrderTime, localDateTime).eq(ExamRegistration::getPaymentState, GooodsOrderStatusEnum.WAIT_PAY);
List<ExamRegistration> goodsOrderList = examRegistrationMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(goodsOrderList)) {
return;
}
TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
//处理商品订单信息
if (CollectionUtils.isNotEmpty(goodsOrderList)) {
processGoodsOrderInfo(goodsOrderList);
}
transactionManager.commit(transactionStatus);
} catch (Exception e) {
transactionManager.rollback(transactionStatus);
log.error("自动关闭订单失败,失败原因为 =====> {}", e.getMessage());
throw e;
}
}
/**
* 自动修改未支付的订单状态每15分钟执行一次
*
* @throws Exception 异常信息
*/
@Override
public void automaticUpdateOrderStatusTask() throws Exception {
//查询未支付的商品订单信息
QueryWrapper<ExamRegistration> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(ExamRegistration::getPaymentState, GooodsOrderStatusEnum.WAIT_PAY);
List<ExamRegistration> goodsOrderList = examRegistrationMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(goodsOrderList)) {
return;
}
//调用微信查单接口查询本地系统中待支付的订单在微信系统中是否已经支付
TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
//修改商品订单状态以及记录支付信息
if (CollectionUtils.isNotEmpty(goodsOrderList)) {
updateGoodsPaymentInfo(goodsOrderList);
}
transactionManager.commit(transactionStatus);
} catch (Exception e) {
log.error("修改订单状态定时任务失败,失败原因为 =====> {}", e.getMessage());
transactionManager.rollback(transactionStatus);
throw e;
}
}
/**
* 处理商品订单信息
*
* @param goodsOrderList 订单列表
* @throws Exception 异常信息
*/
private void processGoodsOrderInfo(List<ExamRegistration> goodsOrderList) throws Exception {
List<String> orderList = Lists.newArrayList();
//微信系统已经关闭的订单信息集合
List<String> closedOrderList = Lists.newArrayList();
for (ExamRegistration goodOrder : goodsOrderList) {
OrderStatusInfoVO vo = new OrderStatusInfoVO();
OrderStatusInfoVO statusInfoVO = weChatPaymentService.queryGoodsOrderStatus(goodOrder.getId(), vo);
if (BooleanUtils.isFalse(statusInfoVO.getPayFlag())) {
closedOrderList.add(goodOrder.getId());
continue;
}
//关闭微信系统订单
int statusCode = weChatPaymentService.closeWeChatOrderInfo(goodOrder.getId());
if (statusCode == SUCCESS_CODE || statusCode == SUCCESS_CODE_TWO) {
orderList.add(goodOrder.getId());
}
}
//关闭系统内部商品订单
if (CollectionUtils.isNotEmpty(closedOrderList)) {
// 说明当前订单在微信系统中已经关闭需要重新确认本地订单状态是否同步更新未更新则需要同步更新
log.info("自动修改商品订单信息状态 ======> [{}]", closedOrderList);
examRegistrationMapper.updateBatchPaymentState(closedOrderList, GooodsOrderStatusEnum.CANCEL.getInfo());
}
}
/**
* 新增商品订单支付信息
*
* @param goodsOrderList 商品订单列表
* @throws Exception 异常信息
*/
private void updateGoodsPaymentInfo(List<ExamRegistration> goodsOrderList) throws Exception {
for (ExamRegistration goodsOrder : goodsOrderList) {
if (Objects.isNull(goodsOrder) || StringUtils.isBlank(goodsOrder.getId())) {
continue;
}
OrderStatusInfoVO vo = new OrderStatusInfoVO();
OrderStatusInfoVO statusInfoVO = weChatPaymentService.queryGoodsOrderStatus(goodsOrder.getId(), vo);
if (StringUtils.isNotBlank(statusInfoVO.getTradeStatus())
&& WeChatTradeStateEnum.SUCCESS.getInfo().equals(statusInfoVO.getTradeStatus())) {
//修改订单状态和记录支付日志信息
insertGoodsOrderPaymentInfo(goodsOrder, statusInfoVO);
}
}
}
/**
* 修改商品订单状态和记录商品订单支付日志信息
*
* @param goodsOrder 商品订单信息
* @param vo 微信查单信息
* @throws Exception 异常信息
*/
private void insertGoodsOrderPaymentInfo(ExamRegistration goodsOrder, OrderStatusInfoVO vo) throws Exception {
//修改商品订单状态和记录支付日志信息
int paymentInfoCount = 0;
if (Objects.nonNull(goodsOrder)) {
examRegistrationMapper.updatePaymentStateById(GooodsOrderStatusEnum.PAY.getInfo(), goodsOrder.getId());
paymentInfoCount = paymentInfoMapper.getByRegistrationId(goodsOrder.getId());
}
if (paymentInfoCount <= 0) {
WeChatQueryOrderVO weChatQueryOrderVO = vo.getWeChatQueryOrderVO();
PaymentInfo paymentInfo = buildPaymentInfo(weChatQueryOrderVO, goodsOrder);
int insertCount = paymentInfoMapper.insertPaymentInfo(paymentInfo);
if (insertCount <= 0) {
log.error("记录商品或者学习培训支付订单支付日志出错,参数为 ====> [{}]", paymentInfo);
throw new SerialException("记录商品或者学习培训支付订单支付日志出错,请联系管理员!");
}
}
}
/**
* 构建支付信息参数
*
* @param weChatQueryOrderVO 微信查单返回结果信息
* @param goodsOrderInfo 商品订单信息
*/
public PaymentInfo buildPaymentInfo(WeChatQueryOrderVO weChatQueryOrderVO, ExamRegistration goodsOrderInfo) {
PaymentInfo paymentInfo = new PaymentInfo();
if (Objects.nonNull(goodsOrderInfo)) {
paymentInfo.setUserId(Objects.isNull(goodsOrderInfo.getUserId()) ? null : goodsOrderInfo.getUserId());
paymentInfo.setExamRegistrationId(StringUtils.isBlank(goodsOrderInfo.getId()) ? "" : goodsOrderInfo.getId());
}
paymentInfo.setTransactionNo(StringUtils.isBlank(weChatQueryOrderVO.getTransactionId()) ? "" : weChatQueryOrderVO.getTransactionId());
paymentInfo.setPayPrice(BigDecimal.valueOf(Objects.isNull(weChatQueryOrderVO.getAmount().getPayerTotal()) ? 0 : weChatQueryOrderVO.getAmount().getPayerTotal()).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_DOWN));
paymentInfo.setPayType(PayTypeEnum.WECHAT_PAY.getInfo());
paymentInfo.setWechatTradeState(StringUtils.isBlank(weChatQueryOrderVO.getTradeState()) ? "" : weChatQueryOrderVO.getTradeState());
paymentInfo.setPayNotifyContent(JSON.toJSON(weChatQueryOrderVO).toString());
paymentInfo.setPayTime(LocalDateTime.parse(StringUtils.isBlank(weChatQueryOrderVO.getSuccessTime()) ? "" : weChatQueryOrderVO.getSuccessTime(), DateTimeFormatter.ISO_DATE_TIME));
if (weChatQueryOrderVO.getMchid().equals(xylWeChatPaymentConfig.getXylMchId())) {
paymentInfo.setPaymentMerchantType("WEISHENGXUEHUI");
}
paymentInfo.setDelFlag(0);
paymentInfo.setCreateTime(new Date());
return paymentInfo;
}
}

View File

@ -0,0 +1,15 @@
package com.yf.exam.modules.quartz.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @description: 考试报名定时任务模拟考试到期后自动关闭订单
* @author: haown
* @create: 2025-08-25 10:25
**/
@Slf4j
@Component("examRegistrationTask")
public class ExamRegistrationTask {
}

View File

@ -0,0 +1,47 @@
package com.yf.exam.modules.quartz.task;
import com.yf.exam.modules.quartz.service.PaymentInfoTaskService;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* @Description 支付定时任务类
* @Author 纪寒
* @Date 2022-10-21 14:34:36
* @Version 1.0
*/
@Slf4j
@Component("paymentTask")
public class PaymentInfoTask {
@Resource
private PaymentInfoTaskService paymentInfoTaskService;
/**
* 自动关闭商品订单2小时未支付的订单信息每15分钟执行一次
*
* @throws Exception 异常信息
*/
@Scheduled(cron = "0 0/15 * * * ?")
public void automaticOrderTask() throws Exception {
log.info("开始执行自动关闭订单定时任务......");
paymentInfoTaskService.automaticOrderTask();
log.info("完成自动关闭订单定时任务......");
}
/**
* 自动修改未支付的订单状态每15分钟执行一次
* 该定时任务用与接收微信支付回调通知失败或者网络原因导致微信支付系统支付成功但是本地订单状态未同步更新的情况
*
* @throws Exception 异常信息
*/
@Scheduled(cron = "0 0/2 * * * ?")
public void automaticUpdateOrderStatusTask() throws Exception {
log.info("开始执行修改订单状态定时任务......");
paymentInfoTaskService.automaticUpdateOrderStatusTask();
log.info("完成修改订单状态定时任务......");
}
}

View File

@ -105,7 +105,7 @@
<select id="getExamList" resultMap="OnlineResultMap">
SELECT * FROM el_exam
where state = 0 and (exam_type = 2 or exam_type = 3)
and id not in (select exam_id from el_exam_registration where user_id = #{query.userId} and payment_state = 'PAY')
and id not in (select exam_id from el_exam_registration where user_id = #{query.userId} and (payment_state = 'PAY' or payment_state = 'WAIT_REFUND'))
<if test="query!=null">
<if test="query.openType!=null">
AND open_type = #{query.openType}

View File

@ -44,28 +44,77 @@
<select id="getRegExamList" resultType="com.yf.exam.modules.exam.dto.response.ExamRegistrationVO">
SELECT reg.user_id as userId, reg.reg_time, reg.payment_state, reg.finish_state, ex.id as examId, ex.title, ex.content
SELECT reg.id, reg.user_id as userId, reg.reg_time, reg.payment_state, reg.payment_date, reg.finish_state, ex.id as examId, ex.title, ex.content
, ex.exam_type, ex.start_date, ex.end_date, ex.start_time, ex.end_time
,ex.total_score, ex.total_time, ex.qualify_score, ex.exam_fee
,ex.total_score, ex.total_time, ex.qualify_score, ex.exam_fee, DATEDIFF(ex.start_date, now()) >= 2 as daysAllow, ex.expiration_date
FROM el_exam_registration reg
left join el_exam ex on reg.exam_id = ex.id
WHERE ex.state=0
<if test="userId!=null">
AND reg.user_id=#{userId}
WHERE ex.state = 0
<if test="query.userId!=null">
AND reg.user_id=#{query.userId}
</if>
<if test="finishState!=null">
AND reg.finish_state=#{finishState}
<if test="query.finishState!=null">
AND reg.finish_state=#{query.finishState}
</if>
<if test="paymentState!=null">
AND reg.payment_state=#{paymentState}
<if test="query.paymentState!=null">
AND reg.payment_state=#{query.paymentState}
</if>
<if test="examType!=null">
AND ex.exam_type=#{examType}
<if test="query.examId!=null">
AND reg.exam_id=#{query.examId}
</if>
<if test="examId!=null">
AND reg.exam_id=#{examId}
<if test="query.examType!=null">
AND ex.exam_type=#{query.examType}
</if>
order by ex.start_date, ex.start_time
<if test="query.examTypes != null and query.examTypes != ''">
AND ex.exam_type in
<foreach item = "item" collection="query.examTypes.split(',')" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="query.paymentStates != null and query.paymentStates != ''">
AND reg.payment_state in
<foreach item = "item" collection="query.paymentStates.split(',')" open="(" separator="," close=")">
#{item}
</foreach>
</if>
order by reg.payment_date desc, reg.order_time desc
</select>
<select id="getRegExamPage" resultType="com.yf.exam.modules.exam.dto.response.ExamRegistrationVO">
SELECT reg.id, reg.user_id as userId, reg.reg_time, reg.payment_state, reg.payment_date, reg.finish_state, ex.id as examId, ex.title, ex.content
, ex.exam_type, ex.start_date, ex.end_date, ex.start_time, ex.end_time
,ex.total_score, ex.total_time, ex.qualify_score, ex.exam_fee, ex.expiration_date
FROM el_exam_registration reg
left join el_exam ex on reg.exam_id = ex.id
WHERE ex.state = 0
<if test="query.userId!=null">
AND reg.user_id=#{query.userId}
</if>
<if test="query.finishState!=null">
AND reg.finish_state=#{query.finishState}
</if>
<if test="query.paymentState!=null">
AND reg.payment_state=#{query.paymentState}
</if>
<if test="query.examId!=null">
AND reg.exam_id=#{query.examId}
</if>
<if test="query.examType!=null">
AND ex.exam_type=#{query.examType}
</if>
<if test="query.examTypes != null and query.examTypes != ''">
AND ex.exam_type in
<foreach item = "item" collection="query.examTypes.split(',')" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="query.paymentStates != null and query.paymentStates != ''">
AND reg.payment_state in
<foreach item = "item" collection="query.paymentStates.split(',')" open="(" separator="," close=")">
#{item}
</foreach>
</if>
order by reg.payment_date desc, reg.order_time desc
</select>
<update id="updateFinishState" parameterType="com.yf.exam.modules.exam.entity.ExamRegistration">
@ -79,12 +128,12 @@
</update>
<select id="getRegUserList" resultType="com.yf.exam.modules.exam.dto.response.ExamRegistrationVO">
SELECT reg.user_id as userId, reg.user_name, reg.real_name, reg.phone, reg.payment_state, ex.id as examId, ex.title, ex.content
, ex.exam_type, ex.start_date, ex.end_date, ex.start_time, ex.end_time
SELECT reg.id, reg.user_id as userId, reg.user_name, reg.real_name, reg.phone, reg.payment_state, reg.confirm_refund_status, reg.refund_reason
, ex.id as examId, ex.title, ex.content, ex.exam_type, ex.start_date, ex.end_date, ex.start_time, ex.end_time
,ex.total_score, ex.total_time, ex.qualify_score, ex.exam_fee
FROM el_exam_registration reg
left join el_exam ex on reg.exam_id = ex.id
WHERE ex.state=0
WHERE ex.state=0 and (reg.payment_state = 'PAY' or reg.payment_state = 'WAIT_REFUND' or reg.payment_state = 'REFUNDED')
<if test="query!=null">
<if test="query.title!=null and query.title!=''">
AND ex.title LIKE CONCAT('%',#{query.title},'%')
@ -110,4 +159,51 @@
set payment_state = #{paymentState}
where id= #{id}
</update>
<update id="updateFinishStateById" parameterType="com.yf.exam.modules.exam.entity.ExamRegistration">
update el_exam_registration
set finish_state = #{finishState}, payment_state = 'PAY'
where id= #{id}
</update>
<select id="getMockExamList" resultType="com.yf.exam.modules.exam.dto.response.ExamRegistrationVO">
SELECT
IF(reg.id is null,false,true) as buyState,
ex.id as examId, ex.title, ex.content
, ex.exam_type, ex.start_date, ex.end_date, ex.start_time, ex.end_time
,ex.total_score, ex.total_time, ex.qualify_score, ex.exam_fee,ex.expiration_date,
reg.id as id, reg.user_id as userId, reg.user_name, reg.real_name, reg.phone, reg.payment_state, reg.finish_state
FROM el_exam ex
left join (select * from el_exam_registration where user_id = #{query.userId} and (payment_state = 'PAY' or payment_state = 'WAIT_REFUND')) reg on reg.exam_id = ex.id
WHERE ex.state=0
<if test="query!=null">
<if test="query.title!=null and query.title!=''">
AND ex.title LIKE CONCAT('%',#{query.title},'%')
</if>
<if test="query.examType!=null">
AND ex.exam_type=#{query.examType}
</if>
<if test="query.paymentEndDate!=null">
AND reg.payment_date >= #{query.paymentEndDate}
</if>
</if>
order by ex.start_date
</select>
<update id="updateConfirmRefundStatus" parameterType="string">
update el_exam_registration
set confirm_refund_status = #{confirmRefundStatus},
update_time = now()
where id = #{id}
and confirm_refund_status &lt;&gt; #{confirmRefundStatus}
</update>
<update id="updateBatchPaymentState">
UPDATE el_exam_registration SET payment_state = #{paymentState}, update_time = now()
WHERE
payment_state &lt;&gt; #{paymentState}
AND id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
</mapper>