diff --git a/exam-admin/src/main/java/com/yf/exam/ExamApplication.java b/exam-admin/src/main/java/com/yf/exam/ExamApplication.java index 40c5f64..34f485d 100644 --- a/exam-admin/src/main/java/com/yf/exam/ExamApplication.java +++ b/exam-admin/src/main/java/com/yf/exam/ExamApplication.java @@ -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 { diff --git a/exam-admin/src/main/java/com/yf/exam/config/ShiroConfig.java b/exam-admin/src/main/java/com/yf/exam/config/ShiroConfig.java index 09e081a..cdb2fec 100644 --- a/exam-admin/src/main/java/com/yf/exam/config/ShiroConfig.java +++ b/exam-admin/src/main/java/com/yf/exam/config/ShiroConfig.java @@ -52,6 +52,7 @@ public class ShiroConfig { // 小程序接口 map.put("/examApplet/**", "anon"); + map.put("/exam/weChatPayment/**", "anon"); // 获取网站基本信息 map.put("/exam/api/sys/config/detail", "anon"); diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletExamController.java b/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletExamController.java index 725c146..8493818 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletExamController.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletExamController.java @@ -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 list = examService.getExamList(reqDTO); return super.success(list); } + } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletExamRegistrationController.java b/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletExamRegistrationController.java index 9f24b46..6504305 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletExamRegistrationController.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletExamRegistrationController.java @@ -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 list = baseService.getRegExamList(examRegistrationDTO); return super.success(list); } + + /** + * 模拟考试列表,查询所有的模拟考试,返回是否已购买标识 + * @return + */ + @ApiOperation(value = "模拟考试列表,查询所有的模拟考试,返回是否已购买标识") + @RequestMapping(value = "/getMockExamList", method = { RequestMethod.POST}) + public ApiRest getMockExamList(@RequestBody PagingReqDTO reqDTO) { + //分页查询并转换 + IPage 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)); + } } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/WeChatPaymentController.java b/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletWeChatPaymentController.java similarity index 93% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/controller/WeChatPaymentController.java rename to exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletWeChatPaymentController.java index 5fb93f1..0a89ff4 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/WeChatPaymentController.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/applet/controller/AppletWeChatPaymentController.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/exam/controller/ExamRegistrationController.java b/exam-admin/src/main/java/com/yf/exam/modules/exam/controller/ExamRegistrationController.java index 6a23e54..29aa10b 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/exam/controller/ExamRegistrationController.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/exam/controller/ExamRegistrationController.java @@ -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 list = baseService.getRegExamList(examRegistrationDTO); return super.success(list); } + /** + * 分页查询已报名的考试列表 + * @return + */ + @ApiOperation(value = "分页查询已报名的考试列表") + @RequestMapping(value = "/getRegExamPage", method = {RequestMethod.POST}) + public ApiRest> getRegExamPage(@RequestBody PagingReqDTO reqDTO) { + IPage page = baseService.getRegExamPage(reqDTO); + return super.success(page); + } + /** * 分页查询考生列表 * @return @@ -64,4 +76,15 @@ public class ExamRegistrationController extends BaseController { IPage 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())); + } } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/ExamRegistrationDTO.java b/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/ExamRegistrationDTO.java index 61b9543..be466b7 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/ExamRegistrationDTO.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/ExamRegistrationDTO.java @@ -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; } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/ExamRegistrationUpdDTO.java b/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/ExamRegistrationUpdDTO.java new file mode 100644 index 0000000..a31e4f0 --- /dev/null +++ b/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/ExamRegistrationUpdDTO.java @@ -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; +} diff --git a/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/response/ExamRegistrationVO.java b/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/response/ExamRegistrationVO.java index 5528eea..3442fa2 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/response/ExamRegistrationVO.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/exam/dto/response/ExamRegistrationVO.java @@ -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; } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/exam/entity/ExamRegistration.java b/exam-admin/src/main/java/com/yf/exam/modules/exam/entity/ExamRegistration.java index 1269d43..12206cc 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/exam/entity/ExamRegistration.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/exam/entity/ExamRegistration.java @@ -106,6 +106,12 @@ public class ExamRegistration extends UserAttachment { */ private String confirmRefundStatus; + /** + * 退款原因 + */ + @ApiModelProperty(value = "退款原因") + private String refundReason; + /** * 创建时间 */ diff --git a/exam-admin/src/main/java/com/yf/exam/modules/exam/mapper/ExamRegistrationMapper.java b/exam-admin/src/main/java/com/yf/exam/modules/exam/mapper/ExamRegistrationMapper.java index 2c2c08f..5951970 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/exam/mapper/ExamRegistrationMapper.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/exam/mapper/ExamRegistrationMapper.java @@ -19,11 +19,35 @@ import org.apache.ibatis.annotations.Param; */ public interface ExamRegistrationMapper extends BaseMapper { - List getRegExamList(ExamRegistrationDTO examRegistrationDTO); + List getRegExamList(@Param("query") ExamRegistrationDTO examRegistrationDTO); + + IPage getRegExamPage(Page page, @Param("query") ExamRegistrationDTO examRegistrationDTO); void updateFinishState(ExamRegistration examRegistration); IPage getRegUserList(Page page, @Param("query") ExamRegistrationDTO examRegistrationDTO); int updatePaymentStateById(String paymentState, String id); + + int updateFinishStateById(Integer finishState, String id); + + IPage 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 ids, @Param("paymentState") String paymentState); } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/exam/service/ExamRegistrationService.java b/exam-admin/src/main/java/com/yf/exam/modules/exam/service/ExamRegistrationService.java index c904495..27bfd20 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/exam/service/ExamRegistrationService.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/exam/service/ExamRegistrationService.java @@ -33,6 +33,15 @@ public interface ExamRegistrationService extends IService { */ List getRegExamList(ExamRegistrationDTO examRegistrationDTO); + /** + * @description 分页查询已报名的考试列表 + * @Param + * @return null + * @Author haown + * @Date 2025-8-21 16:27 + */ + IPage getRegExamPage(PagingReqDTO reqDTO); + void updateFinishState(ExamRegistration examRegistration); /** @@ -44,4 +53,16 @@ public interface ExamRegistrationService extends IService { */ IPage getRegUserList(PagingReqDTO examRegistrationDTO); + int updateFinishStateById(Integer finishState, String id); + + IPage getMockExamList(PagingReqDTO examRegistrationDTO); + + /** + * @description 修改报名状态(退款) + * @Param + * @Author haown + * @Date 2025-8-20 15:43 + */ + int updateRegRefound(ExamRegistration examRegistration); + } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/exam/service/impl/ExamRegistrationServiceImpl.java b/exam-admin/src/main/java/com/yf/exam/modules/exam/service/impl/ExamRegistrationServiceImpl.java index aad2f81..767fbb4 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/exam/service/impl/ExamRegistrationServiceImpl.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/exam/service/impl/ExamRegistrationServiceImpl.java @@ -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 getRegExamPage(PagingReqDTO reqDTO) { + // 创建分页对象 + Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); + + // 查找分页 + IPage 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 getMockExamList(PagingReqDTO reqDTO) { + // 创建分页对象 + Page page = new Page(reqDTO.getCurrent(), reqDTO.getSize()); + + // 查找分页 + IPage 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); + } } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/payment/controller/WeChatPaymentController.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/controller/WeChatPaymentController.java new file mode 100644 index 0000000..76eea44 --- /dev/null +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/controller/WeChatPaymentController.java @@ -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); + } + +} diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/dto/PaymentDTO.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/dto/PaymentDTO.java similarity index 97% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/dto/PaymentDTO.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/dto/PaymentDTO.java index 2c0264f..605847f 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/dto/PaymentDTO.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/dto/PaymentDTO.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/dto/RefundDTO.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/dto/RefundDTO.java similarity index 96% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/dto/RefundDTO.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/dto/RefundDTO.java index c4b26a3..2bce230 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/dto/RefundDTO.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/dto/RefundDTO.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatPayNotifyService.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatPayNotifyService.java similarity index 95% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatPayNotifyService.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatPayNotifyService.java index e59d738..3b0b878 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatPayNotifyService.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatPayNotifyService.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatPaymentService.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatPaymentService.java similarity index 91% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatPaymentService.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatPaymentService.java index f5c7d57..a0c4482 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatPaymentService.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatPaymentService.java @@ -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支付业务层 diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatRefundService.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatRefundService.java similarity index 79% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatRefundService.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatRefundService.java index c0b7132..bdaa019 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/WeChatRefundService.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/WeChatRefundService.java @@ -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); + /** * 微信确认退款接口 * diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatPayNotifyServiceImpl.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatPayNotifyServiceImpl.java similarity index 97% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatPayNotifyServiceImpl.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatPayNotifyServiceImpl.java index 5fcbcc1..1f3d883 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatPayNotifyServiceImpl.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatPayNotifyServiceImpl.java @@ -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()); } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatPaymentServiceImpl.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatPaymentServiceImpl.java similarity index 98% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatPaymentServiceImpl.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatPaymentServiceImpl.java index 2fde217..5320e32 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatPaymentServiceImpl.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatPaymentServiceImpl.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatRefundServiceImpl.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatRefundServiceImpl.java similarity index 84% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatRefundServiceImpl.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatRefundServiceImpl.java index 22cad10..a8ca48f 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/service/impl/WeChatRefundServiceImpl.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/service/impl/WeChatRefundServiceImpl.java @@ -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); } diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/vo/OrderStatusInfoVO.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/vo/OrderStatusInfoVO.java similarity index 94% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/vo/OrderStatusInfoVO.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/vo/OrderStatusInfoVO.java index ae97ff2..86fdef0 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/vo/OrderStatusInfoVO.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/vo/OrderStatusInfoVO.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatAppletSignVO.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatAppletSignVO.java similarity index 95% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatAppletSignVO.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatAppletSignVO.java index d382f7b..d0338ca 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatAppletSignVO.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatAppletSignVO.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatPayNotifyPlaintextVO.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatPayNotifyPlaintextVO.java similarity index 99% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatPayNotifyPlaintextVO.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatPayNotifyPlaintextVO.java index 4effef6..00256a7 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatPayNotifyPlaintextVO.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatPayNotifyPlaintextVO.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatQueryOrderVO.java b/exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatQueryOrderVO.java similarity index 98% rename from exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatQueryOrderVO.java rename to exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatQueryOrderVO.java index 0cc7ba2..d06b802 100644 --- a/exam-admin/src/main/java/com/yf/exam/modules/applet/vo/WeChatQueryOrderVO.java +++ b/exam-admin/src/main/java/com/yf/exam/modules/payment/vo/WeChatQueryOrderVO.java @@ -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; diff --git a/exam-admin/src/main/java/com/yf/exam/modules/quartz/controller/PaymentInfoTaskController.java b/exam-admin/src/main/java/com/yf/exam/modules/quartz/controller/PaymentInfoTaskController.java new file mode 100644 index 0000000..8e70d1c --- /dev/null +++ b/exam-admin/src/main/java/com/yf/exam/modules/quartz/controller/PaymentInfoTaskController.java @@ -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(); + } +} diff --git a/exam-admin/src/main/java/com/yf/exam/modules/quartz/service/PaymentInfoTaskService.java b/exam-admin/src/main/java/com/yf/exam/modules/quartz/service/PaymentInfoTaskService.java new file mode 100644 index 0000000..308ca14 --- /dev/null +++ b/exam-admin/src/main/java/com/yf/exam/modules/quartz/service/PaymentInfoTaskService.java @@ -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; +} diff --git a/exam-admin/src/main/java/com/yf/exam/modules/quartz/service/impl/PaymentInfoTaskServiceImpl.java b/exam-admin/src/main/java/com/yf/exam/modules/quartz/service/impl/PaymentInfoTaskServiceImpl.java new file mode 100644 index 0000000..a51a97a --- /dev/null +++ b/exam-admin/src/main/java/com/yf/exam/modules/quartz/service/impl/PaymentInfoTaskServiceImpl.java @@ -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 wrapper = new QueryWrapper<>(); + wrapper.lambda().le(ExamRegistration::getOrderTime, localDateTime).eq(ExamRegistration::getPaymentState, GooodsOrderStatusEnum.WAIT_PAY); + List 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 wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(ExamRegistration::getPaymentState, GooodsOrderStatusEnum.WAIT_PAY); + List 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 goodsOrderList) throws Exception { + List orderList = Lists.newArrayList(); + //微信系统已经关闭的订单信息集合 + List 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 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; + } + +} diff --git a/exam-admin/src/main/java/com/yf/exam/modules/quartz/task/ExamRegistrationTask.java b/exam-admin/src/main/java/com/yf/exam/modules/quartz/task/ExamRegistrationTask.java new file mode 100644 index 0000000..f109c27 --- /dev/null +++ b/exam-admin/src/main/java/com/yf/exam/modules/quartz/task/ExamRegistrationTask.java @@ -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 { + +} diff --git a/exam-admin/src/main/java/com/yf/exam/modules/quartz/task/PaymentInfoTask.java b/exam-admin/src/main/java/com/yf/exam/modules/quartz/task/PaymentInfoTask.java new file mode 100644 index 0000000..058280b --- /dev/null +++ b/exam-admin/src/main/java/com/yf/exam/modules/quartz/task/PaymentInfoTask.java @@ -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("完成修改订单状态定时任务......"); + } + +} diff --git a/exam-admin/src/main/resources/mapper/exam/ExamMapper.xml b/exam-admin/src/main/resources/mapper/exam/ExamMapper.xml index 1180f2d..1866645 100644 --- a/exam-admin/src/main/resources/mapper/exam/ExamMapper.xml +++ b/exam-admin/src/main/resources/mapper/exam/ExamMapper.xml @@ -105,7 +105,7 @@ - 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 - - AND reg.user_id=#{userId} + WHERE ex.state = 0 + + AND reg.user_id=#{query.userId} - - AND reg.finish_state=#{finishState} + + AND reg.finish_state=#{query.finishState} - - AND reg.payment_state=#{paymentState} + + AND reg.payment_state=#{query.paymentState} - - AND ex.exam_type=#{examType} + + AND reg.exam_id=#{query.examId} - - AND reg.exam_id=#{examId} + + AND ex.exam_type=#{query.examType} - order by ex.start_date, ex.start_time + + AND ex.exam_type in + + #{item} + + + + AND reg.payment_state in + + #{item} + + + order by reg.payment_date desc, reg.order_time desc + + + @@ -79,12 +128,12 @@ + 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 + + + AND ex.title LIKE CONCAT('%',#{query.title},'%') + + + AND ex.exam_type=#{query.examType} + + + AND reg.payment_date >= #{query.paymentEndDate} + + + order by ex.start_date + + + + update el_exam_registration + set confirm_refund_status = #{confirmRefundStatus}, + update_time = now() + where id = #{id} + and confirm_refund_status <> #{confirmRefundStatus} + + + + UPDATE el_exam_registration SET payment_state = #{paymentState}, update_time = now() + WHERE + payment_state <> #{paymentState} + AND id IN + + #{id} + +