Przeglądaj źródła

初始化项目

wangzhengliang 4 lat temu
commit
cc33d3d1e4
92 zmienionych plików z 8110 dodań i 0 usunięć
  1. 163 0
      pom.xml
  2. 22 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/TuitionPaymentApplication.java
  3. 14 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/anno/AdminLoginCheck.java
  4. 60 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/aop/LoginCheckAspect.java
  5. 55 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/exception/BizCodeEnume.java
  6. 124 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/exception/MyExceptionHandler.java
  7. 65 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/exception/RRException.java
  8. 35 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/BaseResult.java
  9. 47 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/CommonResult.java
  10. 127 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/Constant.java
  11. 141 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/JwtOperator.java
  12. 55 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/MyQuery.java
  13. 33 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/PageParam.java
  14. 101 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/PageUtils.java
  15. 51 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/common/xss/SQLFilter.java
  16. 25 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/config/CCBPayConfig.java
  17. 31 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/config/CorsConfig.java
  18. 20 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/config/JXNXSPayConfig.java
  19. 21 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/config/WechatConfig.java
  20. 21 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/config/WeixiaoConfig.java
  21. 133 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/AdminInfoController.java
  22. 75 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/EnablePayLimitByGradeController.java
  23. 152 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/FeedbackMsgController.java
  24. 119 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/PayController.java
  25. 94 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/PayMethodSettingController.java
  26. 155 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/PayOrderController.java
  27. 175 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/PayableInfoController.java
  28. 54 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/TestController.java
  29. 119 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/WechatController.java
  30. 44 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/conver/aliexcel/CustomPayMethodConverter.java
  31. 38 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/conver/aliexcel/LocalDateTimeConverter.java
  32. 17 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/AdminInfoDao.java
  33. 16 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/EnablePayLimitByGradeDao.java
  34. 17 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/FeedbackMsgDao.java
  35. 9 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/PayMethodSettingDao.java
  36. 17 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/PayOrderDao.java
  37. 17 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/PayableInfoDao.java
  38. 54 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/AdminInfoEntity.java
  39. 31 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/EnablePayLimitByGradeEntity.java
  40. 64 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/FeedbackMsgEntity.java
  41. 44 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/PayMethodSettingEntity.java
  42. 90 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/PayOrderEntity.java
  43. 75 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/PayableInfoEntity.java
  44. 22 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/enume/FeedbackMsgStatus.java
  45. 22 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/enume/PayMethod.java
  46. 28 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/enume/PayStatus.java
  47. 47 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/listener/aliexcel/MyPayableInfoImportListener.java
  48. 32 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/listener/aliexcel/MyStudentNoImportListener.java
  49. 22 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/AdminInfoService.java
  50. 16 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/EnablePayLimitByGradeService.java
  51. 24 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/FeedbackMsgService.java
  52. 9 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/PayMethodSettingService.java
  53. 50 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/PayOrderService.java
  54. 23 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/PayService.java
  55. 33 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/PayableInfoService.java
  56. 68 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/AdminInfoServiceImpl.java
  57. 31 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/EnablePayLimitByGradeServiceImpl.java
  58. 71 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/FeedbackMsgServiceImpl.java
  59. 30 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/PayMethodSettingServiceImpl.java
  60. 369 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/PayOrderServiceImpl.java
  61. 453 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/PayServiceImpl.java
  62. 144 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/PayableInfoServiceImpl.java
  63. 57 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/to/DeletePayableInfoTO.java
  64. 33 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/to/FeedbackMsgCreateTO.java
  65. 39 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/to/PayOrderQueryTO.java
  66. 95 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/CCBRSASig.java
  67. 164 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/JXNXSPayUtil.java
  68. 110 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/MyAES.java
  69. 96 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/MySignatureUtil.java
  70. 38 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/MyUtil.java
  71. 34 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/CreateOrderResultVO.java
  72. 20 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/CurrentPayMethodVO.java
  73. 26 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/OrderStatusVO.java
  74. 32 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/PayOrderListVO.java
  75. 21 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/StudentNoVO.java
  76. 21 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/StudentPayableInfoVO.java
  77. 28 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/UserTokenVO.java
  78. 44 0
      src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/WechatJsApiPayParamVO.java
  79. 58 0
      src/main/resources/application.yml
  80. 16 0
      src/main/resources/mapper/tuitionpayment/AdminInfoDao.xml
  81. 14 0
      src/main/resources/mapper/tuitionpayment/EnablePayLimitByGradeDao.xml
  82. 17 0
      src/main/resources/mapper/tuitionpayment/FeedbackMsgDao.xml
  83. 13 0
      src/main/resources/mapper/tuitionpayment/PayMethodSettingDao.xml
  84. 21 0
      src/main/resources/mapper/tuitionpayment/PayOrderDao.xml
  85. 23 0
      src/main/resources/mapper/tuitionpayment/PayableInfoDao.xml
  86. 83 0
      src/main/resources/smart-doc.json
  87. 7 0
      src/main/resources/static/doc/AllInOne.css
  88. 1962 0
      src/main/resources/static/doc/AllInOne.md
  89. 384 0
      src/main/resources/static/doc/index.html
  90. 327 0
      src/main/resources/static/doc/search.js
  91. BIN
      src/main/resources/static/doc/缴费系统接口文档.pdf
  92. 13 0
      src/test/java/com/chuanghai/smartschool/tuitionpayment/TuitionPaymentApplicationTests.java

+ 163 - 0
pom.xml

@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.5.3</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.chuanghai.smartschool</groupId>
+    <artifactId>tuition_payment</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>tuition_payment</name>
+    <description>学费以及杂费缴费项目</description>
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <scope>runtime</scope>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- mybatis plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.2.0</version>
+        </dependency>
+
+        <!-- jwt -->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.10.7</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.10.7</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.10.7</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>3.16.1</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <!-- 数据校验 -->
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <version>6.1.5.Final</version>
+        </dependency>
+
+        <!-- aop -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <!-- excel操作 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>2.2.10</version>
+        </dependency>
+
+        <!-- 建行支付工具类 -->
+        <dependency>
+            <groupId>com.ccb.pay</groupId>
+            <artifactId>netpay</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <!-- 密码加密 -->
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-crypto</artifactId>
+            <version>5.4.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+
+            <!-- smart doc -->
+            <plugin>
+                <groupId>com.github.shalousun</groupId>
+                <artifactId>smart-doc-maven-plugin</artifactId>
+                <version>2.1.4</version>
+                <configuration>
+                    <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
+                    <configFile>./src/main/resources/smart-doc.json</configFile>
+                    <!--指定项目名称-->
+                    <projectName>学费缴纳项目</projectName>
+                </configuration>
+                <executions>
+                    <execution>
+                        <!--如果不需要在执行编译时启动smart-doc,则将phase注释掉-->
+                        <phase>compile</phase>
+                        <goals>
+                            <!--smart-doc提供了html、openapi、markdown等goal,可按需配置-->
+                            <goal>html</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 22 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/TuitionPaymentApplication.java

@@ -0,0 +1,22 @@
+package com.chuanghai.smartschool.tuitionpayment;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class TuitionPaymentApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(TuitionPaymentApplication.class, args);
+    }
+
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
+        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
+        return paginationInterceptor;
+    }
+}

+ 14 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/anno/AdminLoginCheck.java

@@ -0,0 +1,14 @@
+package com.chuanghai.smartschool.tuitionpayment.anno;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * @Author: codingliang
+ * @Description: 管理员登录检查
+ * @Date: 2021-05-14 17:26
+ * @Version: V1.0
+ **/
+@Target(ElementType.METHOD)
+public @interface AdminLoginCheck {
+}

+ 60 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/aop/LoginCheckAspect.java

@@ -0,0 +1,60 @@
+package com.chuanghai.smartschool.tuitionpayment.aop;
+
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.JwtOperator;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @Author: codingliang
+ * @Description: 登录拦截
+ * @Date: 2021-04-29 15:57
+ * @Version: V1.0
+ **/
+@Aspect
+@Component
+public class LoginCheckAspect {
+
+    @Autowired
+    private JwtOperator jwtOperator;
+
+    /**
+     * 管理员登录拦截
+     * @param point
+     * @return
+     * @throws Throwable
+     */
+    @Around("@annotation(com.chuanghai.smartschool.tuitionpayment.anno.AdminLoginCheck)")
+    public Object userLoginCheck(ProceedingJoinPoint point) throws Throwable {
+        validateToken("admin_token");
+        return point.proceed();
+    }
+
+
+    // 检验token
+    private String validateToken(String headName) {
+        HttpServletRequest request = getHttpServletRequest();
+        String token = request.getHeader(headName);
+        if (!StringUtils.hasText(token) || !jwtOperator.validateToken(token)) {
+            throw new RRException(BizCodeEnume.TOKET_INVALID);
+        }
+        return token;
+    }
+
+    // 获取request
+    private HttpServletRequest getHttpServletRequest() {
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
+        return attributes.getRequest();
+    }
+}

+ 55 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/exception/BizCodeEnume.java

@@ -0,0 +1,55 @@
+package com.chuanghai.smartschool.tuitionpayment.common.exception;
+
+/***
+ * 错误码和错误信息定义类
+ * 1. 错误码定义规则为5为数字
+ * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
+ * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
+ * 错误码列表:
+ *  10: 通用
+ *      001:参数格式校验
+ *  11: 用户
+ *  12: 权限
+ *  13: 参数异常
+ *  14: 文件
+ *  15: 积分模块
+ *
+ *
+ */
+public enum BizCodeEnume {
+    UNKNOW_EXCEPTION(10000,"系统未知异常"),
+    VAILD_EXCEPTION(10001,"参数格式校验失败"),
+    BODY_IS_EMPTY(10002, "body为空"),
+    METHOD_NOT_SUPPORT(10003, "请求方法不支持"),
+    DATA_IS_EXIST(10004, "数据已存在"),
+    DATA_IS_NOT_EXIST(10005, "数据不存在"),
+    TOKET_INVALID(10006, "无效的token"),
+    PARAMETER_ERROR(10007, "参数异常"),
+    NO_PERMISSION(10008, "没有操作权限"),
+    ADMIN_LOGIN_FAIL(10009, "管理员登录失败"),
+    REQUEST_HEADER_MISSING(10012, "缺少必要的请求头"),
+    PAYABLE_INFO_IS_NOT_EXIT(10013, "缴费信息不存在"),
+    ORDER_IS_NOT_EXIT(10014, "订单不存在"),
+    GET_JSAPI_PAY_PARAM_ERROR(10015, "获取微信jsapi支付参数失败"),
+    CCB_ORDER_QUERY_ERROR(10016, "建行订单状态查询失败"),
+    NOT_CCB_PAY_ORDER(10017, "非建行支付订单"),
+    JXNXS_ORDER_QUERY_ERROR(10018, "农商行订单状态查询失败"),
+    PAY_GRADE_IS_NOT_EXIT(10019, "缴费年级不存在"),
+    FILE_IMPORT_ERROR(10020, "文件导入失败"),
+    BATCH_EXECUTOR_ERROR(10100, "批量操作数据库失败");
+
+    private int code;
+    private String msg;
+    BizCodeEnume(int code,String msg){
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 124 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/exception/MyExceptionHandler.java

@@ -0,0 +1,124 @@
+package com.chuanghai.smartschool.tuitionpayment.common.exception;
+
+import com.alibaba.excel.exception.ExcelAnalysisException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.CommonResult;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.util.StringUtils;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingRequestHeaderException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author: codingliang
+ * @Description: 统一异常处理
+ * @Date: 2021-01-06 10:07
+ * @Version: V1.0
+ **/
+@RestControllerAdvice
+public class MyExceptionHandler {
+
+    /**
+     * 处理参数校验异常
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public CommonResult handleValidException(MethodArgumentNotValidException e) {
+        Map<String, String> map = new HashMap<>();
+        e.getBindingResult().getFieldErrors().forEach(item -> {
+            String errMessage = item.getDefaultMessage();
+            String errField = item.getField();
+            map.put(errField, errMessage);
+        });
+        return CommonResult.fail(Integer.toString(BizCodeEnume.VAILD_EXCEPTION.getCode()), BizCodeEnume.VAILD_EXCEPTION.getMsg()).setResult(map);
+    }
+
+    /**
+     * 数据库索引重复
+     * @return
+     */
+    @ExceptionHandler(DuplicateKeyException.class)
+    public CommonResult handleSQLIntegrityConstraintViolationException() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.DATA_IS_EXIST.getCode()), BizCodeEnume.DATA_IS_EXIST.getMsg());
+    }
+
+    /**
+     * 请求方法不支持
+     * @return
+     */
+    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+    public CommonResult handleRequestMethodNotSupported() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.METHOD_NOT_SUPPORT.getCode()), BizCodeEnume.METHOD_NOT_SUPPORT.getMsg());
+    }
+
+    /**
+     * 缺少请求头
+     * @return
+     */
+    @ExceptionHandler(MissingRequestHeaderException.class)
+    public CommonResult handleRequestHeaderMissing() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.REQUEST_HEADER_MISSING.getCode()), BizCodeEnume.REQUEST_HEADER_MISSING.getMsg());
+    }
+
+    /**
+     * 参数缺失
+     * @return
+     */
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    public CommonResult handleMissingServletRequestParameterException() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.PARAMETER_ERROR.getCode()), BizCodeEnume.PARAMETER_ERROR.getMsg());
+    }
+
+    /**
+     * body为空
+     * @return
+     */
+    @ExceptionHandler(HttpMessageNotReadableException.class)
+    public CommonResult handleMessageNotReadableException() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.BODY_IS_EMPTY.getCode()), BizCodeEnume.BODY_IS_EMPTY.getMsg());
+    }
+
+    /**
+     * easyExcel 文件读取失败
+     * @return
+     */
+    @ExceptionHandler(ExcelAnalysisException.class)
+    public CommonResult handleExcelAnalysisException(ExcelAnalysisException e) {
+        String errorMessage = e.getMessage();
+        String errMsg = "文件导入失败";
+        if (StringUtils.hasText(errorMessage) && errorMessage.contains("doesn't have a default value")) {
+            errMsg =  "文件导入失败,请检查模板header是否正确";
+        } else if (StringUtils.hasText(errorMessage) && errorMessage.contains("Duplicate entry")) {
+            errMsg = "文件导入失败,含有重复数据";
+        }
+        return CommonResult.fail(Integer.toString(BizCodeEnume.FILE_IMPORT_ERROR.getCode()), errMsg);
+    }
+
+    /**
+     * 自定义异常
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(RRException.class)
+    public CommonResult handleRRException(RRException e) {
+        return CommonResult.fail(Integer.toString(e.getCode()), e.getMsg());
+    }
+
+    /**
+     * 未知异常
+     * @param throwable
+     * @return
+     */
+    @ExceptionHandler(Throwable.class)
+    public CommonResult handleException(Throwable throwable) {
+        throwable.printStackTrace();
+        return CommonResult.fail(Integer.toString(BizCodeEnume.UNKNOW_EXCEPTION.getCode()), BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
+    }
+}

+ 65 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/exception/RRException.java

@@ -0,0 +1,65 @@
+package com.chuanghai.smartschool.tuitionpayment.common.exception;
+
+/**
+ * 自定义异常
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class RRException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+	
+    private String msg;
+    private int code = 500;
+
+    public RRException(String msg) {
+		super(msg);
+		this.msg = msg;
+	}
+	
+	public RRException(String msg, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+	}
+	
+	public RRException(String msg, int code) {
+		super(msg);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public RRException(BizCodeEnume bizCodeEnume) {
+		super(bizCodeEnume.getMsg());
+		this.msg = bizCodeEnume.getMsg();
+		this.code = bizCodeEnume.getCode();
+	}
+
+	public RRException(BizCodeEnume bizCodeEnume, String msg) {
+		super(bizCodeEnume.getMsg());
+		this.msg = msg;
+		this.code = bizCodeEnume.getCode();
+	}
+	
+	public RRException(String msg, int code, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {
+		this.msg = msg;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public void setCode(int code) {
+		this.code = code;
+	}
+	
+	
+}

+ 35 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/BaseResult.java

@@ -0,0 +1,35 @@
+package com.chuanghai.smartschool.tuitionpayment.common.utils;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @Author: codingliang
+ * @Description: 接口统一返回基类
+ * @Date: 2021-04-29 12:09
+ * @Version: V1.0
+ **/
+@Data
+public abstract class BaseResult<T> implements Serializable {
+
+    /**
+     * 接口调用结果标识
+     */
+    private boolean success = false;
+
+    /**
+     * 接口调用结果信息
+     */
+    private String message;
+
+    /**
+     * 接口调用业务码
+     */
+    private String code;
+
+    /**
+     * 接口调用返回数据
+     */
+    private T data;
+}

+ 47 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/CommonResult.java

@@ -0,0 +1,47 @@
+package com.chuanghai.smartschool.tuitionpayment.common.utils;
+
+import java.io.Serializable;
+
+/**
+ * @Author: codingliang
+ * @Description: 接口统一返回
+ * @Date: 2021-04-29 12:10
+ * @Version: V1.0
+ **/
+public class CommonResult<T> extends BaseResult implements Serializable {
+
+    private static final long serialVersionUID = 3616484754899974346L;
+
+    public static CommonResult ok() {
+        return ok("1", "执行成功");
+    }
+
+    public static <T> CommonResult<T> ok(String code, String msg) {
+        return baseCreate(code, msg, true);
+    }
+
+    public static CommonResult fail() {
+        return fail("-1", "执行失败");
+    }
+
+    public static CommonResult fail(String code, String msg) {
+        return baseCreate(code, msg, false);
+    }
+
+    private static <T> CommonResult<T> baseCreate(String code, String msg, boolean success) {
+        CommonResult result = new CommonResult();
+        result.setCode(code);
+        result.setSuccess(success);
+        result.setMessage(msg);
+        return result;
+    }
+
+    public CommonResult<T> setResult(T data) {
+        this.setData(data);
+        return this;
+    }
+
+    public T getData() {
+        return (T) super.getData();
+    }
+}

+ 127 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/Constant.java

@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.chuanghai.smartschool.tuitionpayment.common.utils;
+
+/**
+ * 常量
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class Constant {
+	/** 超级管理员ID */
+	public static final int SUPER_ADMIN = 1;
+    /**
+     * 当前页码
+     */
+    public static final String PAGE = "page";
+    /**
+     * 每页显示记录数
+     */
+    public static final String LIMIT = "limit";
+    /**
+     * 排序字段
+     */
+    public static final String ORDER_FIELD = "sidx";
+    /**
+     * 排序方式
+     */
+    public static final String ORDER = "order";
+    /**
+     *  升序
+     */
+    public static final String ASC = "asc";
+	/**
+	 * 菜单类型
+	 * 
+	 * @author chenshun
+	 * @email sunlightcs@gmail.com
+	 * @date 2016年11月15日 下午1:24:29
+	 */
+    public enum MenuType {
+        /**
+         * 目录
+         */
+    	CATALOG(0),
+        /**
+         * 菜单
+         */
+        MENU(1),
+        /**
+         * 按钮
+         */
+        BUTTON(2);
+
+        private int value;
+
+        MenuType(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+    
+    /**
+     * 定时任务状态
+     * 
+     * @author chenshun
+     * @email sunlightcs@gmail.com
+     * @date 2016年12月3日 上午12:07:22
+     */
+    public enum ScheduleStatus {
+        /**
+         * 正常
+         */
+    	NORMAL(0),
+        /**
+         * 暂停
+         */
+    	PAUSE(1);
+
+        private int value;
+
+        ScheduleStatus(int value) {
+            this.value = value;
+        }
+        
+        public int getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * 云服务商
+     */
+    public enum CloudService {
+        /**
+         * 七牛云
+         */
+        QINIU(1),
+        /**
+         * 阿里云
+         */
+        ALIYUN(2),
+        /**
+         * 腾讯云
+         */
+        QCLOUD(3);
+
+        private int value;
+
+        CloudService(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+
+}

+ 141 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/JwtOperator.java

@@ -0,0 +1,141 @@
+package com.chuanghai.smartschool.tuitionpayment.common.utils;
+
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * @Author: codingliang
+ * @Description: JWT操作类
+ * @Date: 2020-06-03 11:54
+ * @Version: V1.0
+ **/
+@Slf4j
+@RequiredArgsConstructor
+@Component
+public class JwtOperator {
+
+    /**
+     * 秘钥
+     * - 默认aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt
+     */
+    @Value("${jwt.secret:aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt}")
+    private String secret;
+
+    /**
+     * 有效期,单位秒
+     * - 默认2周
+     */
+    @Value("${jwt.expire-time-in-second:1209600}")
+    private Long expirationTimeInSecond;
+
+    /**
+     * 获取过期时间
+     * @return
+     */
+    public Long getExpirationTimeInSecond() {
+        return this.expirationTimeInSecond;
+    }
+
+    /**
+     * 从token中获取claim
+     *
+     * @param token token
+     * @return claim
+     */
+    public Claims getClaimsFromToken(String token) throws RRException {
+        try {
+            return Jwts.parser()
+                    .setSigningKey(this.secret.getBytes())
+                    .parseClaimsJws(token)
+                    .getBody();
+        } catch (Exception e) {
+            throw new RRException(BizCodeEnume.TOKET_INVALID);
+        }
+    }
+
+    /**
+     * 获取token中的信息
+     * @param token token
+     * @param name 属性名称
+     * @return
+     */
+    public Long getInfoFromToken(String token, String name) {
+        Claims claims = getClaimsFromToken(token);
+        return claims.get(name, Long.class);
+    }
+
+    /**
+     * 获取token的过期时间
+     *
+     * @param token token
+     * @return 过期时间
+     */
+    public Date getExpirationDateFromToken(String token) throws RRException {
+        return getClaimsFromToken(token)
+                .getExpiration();
+    }
+
+    /**
+     * 判断token是否过期
+     *
+     * @param token token
+     * @return 已过期返回true,未过期返回false
+     */
+    private Boolean isTokenExpired(String token) throws RRException {
+        Date expiration = getExpirationDateFromToken(token);
+        return expiration.before(new Date());
+    }
+
+    /**
+     * 计算token的过期时间
+     *
+     * @return 过期时间
+     */
+    private Date getExpirationTime() {
+        return new Date(System.currentTimeMillis() + getExpirationTimeInSecond() * 1000);
+    }
+
+    /**
+     * 为指定用户生成token
+     *
+     * @param claims 用户信息
+     * @return token
+     */
+    public String generateToken(Map<String, Object> claims) {
+        Date createdTime = new Date();
+        Date expirationTime = this.getExpirationTime();
+
+
+        byte[] keyBytes = secret.getBytes();
+        SecretKey key = Keys.hmacShaKeyFor(keyBytes);
+
+        return Jwts.builder()
+                .setClaims(claims)
+                .setIssuedAt(createdTime)
+                .setExpiration(expirationTime)
+                .signWith(key, SignatureAlgorithm.HS256)
+                .compact();
+    }
+
+    /**
+     * 判断token是否非法
+     *
+     * @param token token
+     * @return 未过期返回true,否则返回false
+     */
+    public Boolean validateToken(String token) throws RRException {
+        return !isTokenExpired(token);
+    }
+}

+ 55 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/MyQuery.java

@@ -0,0 +1,55 @@
+package com.chuanghai.smartschool.tuitionpayment.common.utils;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.chuanghai.smartschool.tuitionpayment.common.xss.SQLFilter;
+
+/**
+ * @Author: codingliang
+ * @Description: 分页参数设置
+ * @Date: 2021-04-29 12:51
+ * @Version: V1.0
+ **/
+public class MyQuery<T> {
+
+    public IPage<T> getPage(PageParam params) {
+        return this.getPage(params, null, false);
+    }
+
+    public IPage<T> getPage(PageParam params, String defaultOrderField, boolean isAsc) {
+
+        //分页对象
+        Page<T> page = new Page<>(params.getCurPage(), params.getPageSize());
+
+        //排序字段
+        //防止SQL注入(因为sidx、order是通过拼接SQL实现排序的,会有SQL注入风险)
+        String orderField = SQLFilter.sqlInject(params.getOrderField());
+        String order = params.getOrder();
+
+
+        //前端字段排序
+        if(StringUtils.isNotEmpty(orderField) && StringUtils.isNotEmpty(order)){
+            if("asc".equalsIgnoreCase(order)) {
+                return  page.addOrder(OrderItem.asc(orderField));
+            }else {
+                return page.addOrder(OrderItem.desc(orderField));
+            }
+        }
+
+        // 没有排序字段,则不排序
+        if(StringUtils.isEmpty(defaultOrderField)){
+            return page;
+        }
+
+        //默认排序
+        if(isAsc) {
+            page.addOrder(OrderItem.asc(defaultOrderField));
+        }else {
+            page.addOrder(OrderItem.desc(defaultOrderField));
+        }
+
+        return page;
+    }
+}

+ 33 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/PageParam.java

@@ -0,0 +1,33 @@
+package com.chuanghai.smartschool.tuitionpayment.common.utils;
+
+import lombok.Data;
+
+/**
+ * @Author: codingliang
+ * @Description: 分页参数
+ * @Date: 2021-04-29 12:41
+ * @Version: V1.0
+ **/
+@Data
+public class PageParam {
+
+    /**
+     * 当前页
+     */
+    private Integer curPage = 1;
+
+    /**
+     * 每页大小
+     */
+    private Integer pageSize = 10;
+
+    /**
+     * 排序字段
+     */
+    private String orderField;
+
+    /**
+     * 排序方式,可选值:asc、desc
+     */
+    private String order;
+}

+ 101 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/utils/PageUtils.java

@@ -0,0 +1,101 @@
+package com.chuanghai.smartschool.tuitionpayment.common.utils;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 分页工具类
+ * @author Mark sunlightcs@gmail.com
+ */
+public class PageUtils<T> implements Serializable {
+	private static final long serialVersionUID = 1L;
+	/**
+	 * 总记录数
+	 */
+	private int totalCount;
+	/**
+	 * 每页记录数
+	 */
+	private int pageSize;
+	/**
+	 * 总页数
+	 */
+	private int totalPage;
+	/**
+	 * 当前页数
+	 */
+	private int currPage;
+	/**
+	 * 列表数据
+	 */
+	private List<T> list;
+
+	/**
+	 * 分页
+	 * @param list        列表数据
+	 * @param totalCount  总记录数
+	 * @param pageSize    每页记录数
+	 * @param currPage    当前页数
+	 */
+	public PageUtils(List<T> list, int totalCount, int pageSize, int currPage) {
+		this.list = list;
+		this.totalCount = totalCount;
+		this.pageSize = pageSize;
+		this.currPage = currPage;
+		this.totalPage = (int)Math.ceil((double)totalCount/pageSize);
+	}
+
+	/**
+	 * 分页
+	 */
+	public PageUtils(IPage<T> page) {
+		this.list = page.getRecords();
+		this.totalCount = (int)page.getTotal();
+		this.pageSize = (int)page.getSize();
+		this.currPage = (int)page.getCurrent();
+		this.totalPage = (int)page.getPages();
+	}
+
+	public int getTotalCount() {
+		return totalCount;
+	}
+
+	public void setTotalCount(int totalCount) {
+		this.totalCount = totalCount;
+	}
+
+	public int getPageSize() {
+		return pageSize;
+	}
+
+	public void setPageSize(int pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	public int getTotalPage() {
+		return totalPage;
+	}
+
+	public void setTotalPage(int totalPage) {
+		this.totalPage = totalPage;
+	}
+
+	public int getCurrPage() {
+		return currPage;
+	}
+
+	public void setCurrPage(int currPage) {
+		this.currPage = currPage;
+	}
+
+	public List<?> getList() {
+		return list;
+	}
+
+	public void setList(List<T> list) {
+		this.list = list;
+	}
+	
+}

+ 51 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/common/xss/SQLFilter.java

@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.chuanghai.smartschool.tuitionpayment.common.xss;
+
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import org.springframework.util.StringUtils;
+
+/**
+ * SQL过滤
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class SQLFilter {
+
+    /**
+     * SQL注入过滤
+     * @param str  待验证的字符串
+     */
+    public static String sqlInject(String str){
+        if (StringUtils.hasText(str)) {
+            //去掉'|"|;|\字符
+            str = StringUtils.replace(str, "'", "");
+            str = StringUtils.replace(str, "\"", "");
+            str = StringUtils.replace(str, ";", "");
+            str = StringUtils.replace(str, "\\", "");
+
+            //转换成小写
+            str = str.toLowerCase();
+
+            //非法字符
+            String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};
+
+            //判断是否包含非法字符
+            for(String keyword : keywords){
+                if(str.indexOf(keyword) != -1){
+                    throw new RRException("包含非法字符");
+                }
+            }
+
+            return str;
+        } else {
+            return null;
+        }
+    }
+}

+ 25 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/config/CCBPayConfig.java

@@ -0,0 +1,25 @@
+package com.chuanghai.smartschool.tuitionpayment.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Author: codingliang
+ * @Description: 建行支付配置
+ * @Date: 2021-08-03 17:57
+ * @Version: V1.0
+ **/
+@Configuration
+@ConfigurationProperties(prefix = "ccb")
+@Data
+public class CCBPayConfig {
+
+    private String merchantid;
+    private String posid;
+    private String branchid;
+    private String qupwd;
+    private String pubShort;
+    private String pub;
+    private String wxAppId;
+}

+ 31 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/config/CorsConfig.java

@@ -0,0 +1,31 @@
+package com.chuanghai.smartschool.tuitionpayment.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @Author: codingliang
+ * @Description: 解决跨域问题
+ * @Date: 2021-08-12 16:02
+ * @Version: V1.0
+ **/
+@Configuration
+public class CorsConfig {
+
+    @Bean
+    public WebMvcConfigurer corsConfigurer() {
+        return new WebMvcConfigurer() {
+            @Override
+            public void addCorsMappings(CorsRegistry registry) {
+                registry.addMapping("/**")
+                        .allowedMethods("*")
+                        .allowedOriginPatterns("*")
+                        .allowedHeaders("*")
+                        .allowCredentials(true)
+                        .maxAge(3600);
+            }
+        };
+    }
+}

+ 20 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/config/JXNXSPayConfig.java

@@ -0,0 +1,20 @@
+package com.chuanghai.smartschool.tuitionpayment.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Author: codingliang
+ * @Description: 江西农商行支付配置
+ * @Date: 2021-08-18 10:55
+ * @Version: V1.0
+ **/
+@Configuration
+@ConfigurationProperties(prefix = "jxnxs")
+@Data
+public class JXNXSPayConfig {
+
+    private String openId;
+    private String openKey;
+}

+ 21 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/config/WechatConfig.java

@@ -0,0 +1,21 @@
+package com.chuanghai.smartschool.tuitionpayment.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Author: codingliang
+ * @Description: 微信配置
+ * @Date: 2021-08-12 16:05
+ * @Version: V1.0
+ **/
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "wechat")
+public class WechatConfig {
+
+    private String appid;
+    private String secret;
+    private String fontendUrl;
+}

+ 21 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/config/WeixiaoConfig.java

@@ -0,0 +1,21 @@
+package com.chuanghai.smartschool.tuitionpayment.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Author: codingliang
+ * @Description: 微校相关配置
+ * @Date: 2021-06-21 15:26
+ * @Version: V1.0
+ **/
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "weixiao")
+public class WeixiaoConfig {
+
+    private String appKey;
+    private String appSecret;
+    private String fontendUrl;
+}

+ 133 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/AdminInfoController.java

@@ -0,0 +1,133 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import com.chuanghai.smartschool.tuitionpayment.anno.AdminLoginCheck;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.CommonResult;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.entity.AdminInfoEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.AdminInfoService;
+import com.chuanghai.smartschool.tuitionpayment.vo.UserTokenVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+/**
+ * 管理员
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@RestController
+@RequestMapping("tuitionpayment/admininfo")
+public class AdminInfoController {
+
+    @Autowired
+    private AdminInfoService adminInfoService;
+
+    /**
+     * 管理员登录
+     * @param userName 用户名
+     * @param password 密码
+     * @apiNote 测试管理员账号密码:admin/12345
+     * @return
+     */
+    @PostMapping("login")
+    public CommonResult<UserTokenVO> login(@RequestParam String userName,
+                                           @RequestParam String password) {
+        UserTokenVO tokenVO =  adminInfoService.login(userName, password);
+        return CommonResult.ok().setResult(tokenVO);
+    }
+
+    /**
+     * 管理员列表
+     * @param adminToken 管理员token
+     * @param pageParam 分页参数
+     * @return
+     */
+    @AdminLoginCheck
+    @GetMapping("/list")
+    public CommonResult<PageUtils<AdminInfoEntity>> list(@RequestHeader("admin_token") String adminToken,
+                                                         PageParam pageParam){
+        PageUtils page = adminInfoService.queryPage(pageParam);
+
+        return CommonResult.ok().setResult(page);
+    }
+
+    /**
+     * 新增管理员
+     * @param adminToken 管理员token
+     * @return
+     */
+    @AdminLoginCheck
+    @PostMapping("/save")
+    public CommonResult<String> save(@RequestHeader("admin-token") String adminToken,
+                                     @RequestBody @Valid AdminInfoEntity adminInfo,
+                                     BindingResult result){
+        if (result.hasErrors()) {
+            ObjectError error = result.getAllErrors().get(0);
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, error.getDefaultMessage());
+        }
+        String password = adminInfo.getPassword();
+        if (!StringUtils.hasText(password)) {
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, "密码不能为空");
+        }
+        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        adminInfo.setPassword(passwordEncoder.encode(password));
+        adminInfoService.save(adminInfo);
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 修改管理员
+     * @param adminToken 管理员token
+     */
+    @AdminLoginCheck
+    @PutMapping("/update")
+    public CommonResult<String> update(@RequestHeader("admin-token") String adminToken,
+                                       @RequestBody @Valid AdminInfoEntity adminInfo,
+                                       BindingResult result){
+        if (result.hasErrors()) {
+            ObjectError error = result.getAllErrors().get(0);
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, error.getDefaultMessage());
+        }
+        adminInfo.setPassword(null);
+        adminInfoService.updateById(adminInfo);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除管理员
+     * @param adminToken 管理员token
+     * @param id 管理员id
+     */
+    @AdminLoginCheck
+    @DeleteMapping("/delete/{id}")
+    public CommonResult<String> delete(@RequestHeader("admin-token") String adminToken,
+                                       @PathVariable("id") Long id){
+        AdminInfoEntity adminInfoEntity = adminInfoService.getById(id);
+        if ("0".equals(adminInfoEntity.getStatus())) {
+            throw new RRException(BizCodeEnume.NO_PERMISSION);
+        }
+        adminInfoService.removeById(id);
+        return CommonResult.ok();
+    }
+
+}

+ 75 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/EnablePayLimitByGradeController.java

@@ -0,0 +1,75 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import com.chuanghai.smartschool.tuitionpayment.anno.AdminLoginCheck;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.CommonResult;
+import com.chuanghai.smartschool.tuitionpayment.entity.EnablePayLimitByGradeEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.EnablePayLimitByGradeService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 年级可支付限制设置
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-20 10:02:41
+ * @since 0.1.1
+ */
+@RestController
+@RequestMapping("enablePayLimit")
+public class EnablePayLimitByGradeController {
+
+    @Autowired
+    private EnablePayLimitByGradeService enablePayLimitByGradeService;
+
+    /**
+     * 支付限制列表
+     * @param adminToken 管理员token
+     * @since 0.1.1
+     * @return
+     */
+    @GetMapping("list")
+    @AdminLoginCheck
+    public CommonResult<List<EnablePayLimitByGradeEntity>> list(@RequestHeader("admin_token") String adminToken) {
+        List<EnablePayLimitByGradeEntity> list = enablePayLimitByGradeService.list();
+        return CommonResult.ok().setResult(list);
+    }
+
+    /**
+     * 支付限制设置
+     * @param adminToken 管理员token
+     * @param id 设置id
+     * @param enablePay 是否能支付 1能、2不能
+     * @since 0.1.1
+     * @return
+     */
+    @PutMapping("setting/{settingId}/{enablePay}")
+    @AdminLoginCheck
+    public CommonResult<String> setting(@RequestHeader("admin_token") String adminToken,
+                                        @PathVariable("settingId") Long id,
+                                        @PathVariable("enablePay") String enablePay) {
+        if ("1".equals(enablePay) || "2".equals(enablePay)) {
+            EnablePayLimitByGradeEntity enablePayLimitByGradeEntity = enablePayLimitByGradeService.getById(id);
+            if (enablePayLimitByGradeEntity == null) {
+                throw new RRException(BizCodeEnume.ORDER_IS_NOT_EXIT, "支付限制设置不存在");
+            }
+
+            if (!enablePayLimitByGradeEntity.getEnablePay().equals(enablePay)) {
+                enablePayLimitByGradeEntity.setEnablePay(enablePay);
+                enablePayLimitByGradeService.updateById(enablePayLimitByGradeEntity);
+            }
+            return CommonResult.ok();
+        } else {
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, "enablePay字段只能为1或2");
+        }
+    }
+}

+ 152 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/FeedbackMsgController.java

@@ -0,0 +1,152 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import com.chuanghai.smartschool.tuitionpayment.anno.AdminLoginCheck;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.CommonResult;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.entity.FeedbackMsgEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.FeedbackMsgService;
+import com.chuanghai.smartschool.tuitionpayment.to.FeedbackMsgCreateTO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.time.LocalDateTime;
+
+/**
+ * 反馈信息
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@RestController
+@RequestMapping("tuitionpayment/feedbackmsg")
+public class FeedbackMsgController {
+
+    @Autowired
+    private FeedbackMsgService feedbackMsgService;
+
+    /**
+     * 新增反馈信息
+     * @param cardNumber 当前登录者学号
+     * @param feedbackMsgCreateTO 反馈信息to
+     * @param result 数据校验异常绑定结果
+     * @return
+     */
+    @PostMapping("/save")
+    public CommonResult<String> save(@RequestHeader("card_number") String cardNumber,
+                                     @Valid @RequestBody FeedbackMsgCreateTO feedbackMsgCreateTO,
+                                     BindingResult result){
+        if (result.hasErrors()) {
+            ObjectError error = result.getAllErrors().get(0);
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, error.getDefaultMessage());
+        }
+
+        feedbackMsgService.save(cardNumber, feedbackMsgCreateTO);
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 反馈列表【当前用户】
+     * @param cardNumber
+     * @param pageParam
+     * @return
+     */
+    @GetMapping("/listOfCurrentUser")
+    public CommonResult<PageUtils<FeedbackMsgEntity>> listOfCurrentUser(@RequestHeader("card_number") String cardNumber,
+                                                           PageParam pageParam){
+        PageUtils page = feedbackMsgService.queryPage(cardNumber, pageParam);
+
+        return CommonResult.ok().setResult(page);
+    }
+
+    /**
+     * 反馈信息删除
+     * @param cardNumber 当前登录者学号
+     * @param id 反馈信息id
+     * @return
+     */
+    @DeleteMapping("/delete/{id}")
+    public CommonResult<String> delete(@RequestHeader("card_number") String cardNumber,
+                                       @PathVariable("id") Long id){
+        FeedbackMsgEntity entity = feedbackMsgService.getById(id);
+        if (entity == null) {
+            throw new RRException(BizCodeEnume.DATA_IS_NOT_EXIST, "反馈信息不存在");
+        }
+
+        if (!entity.getFeedbackPersonIdentify().equals(cardNumber)) {
+            throw new RRException(BizCodeEnume.NO_PERMISSION);
+        }
+        feedbackMsgService.removeById(id);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 反馈列表【管理员】
+     * @param adminToken 管理员token
+     * @param status 状态,不传查全部反馈,1查未处理反馈,2查已处理反馈
+     * @param keyword 关键字,支持根据根据反馈者姓名、电话搜索
+     * @param pageParam 分页参数
+     * @since 0.0.1
+     * @apiNote 0.1.0新增keyword关键字查询
+     * @return
+     */
+    @AdminLoginCheck
+    @GetMapping("/list")
+    public CommonResult<PageUtils<FeedbackMsgEntity>> listOfAdmin(@RequestHeader("admin_token") String adminToken,
+                                                                  String status,
+                                                                  String keyword,
+                                                                  PageParam pageParam){
+        PageUtils page = feedbackMsgService.queryPageByStatus(status, keyword, pageParam);
+
+        return CommonResult.ok().setResult(page);
+    }
+
+    /**
+     * 反馈详细信息
+     * @param adminToken 管理员token
+     * @param id 反馈id
+     * @return
+     */
+    @AdminLoginCheck
+    @GetMapping("/info/{id}")
+    public CommonResult<FeedbackMsgEntity> info(@RequestHeader("admin_token") String adminToken,
+                                                @PathVariable("id") Long id){
+		FeedbackMsgEntity feedbackMsg = feedbackMsgService.getById(id);
+
+        return CommonResult.ok().setResult(feedbackMsg);
+    }
+
+    /**
+     * 反馈信息处理
+     * @param adminToken 管理员token
+     * @param id 反馈id
+     * @apiNote 将反馈信息从未处理状态改变为已处理状态
+     * @return
+     */
+    @AdminLoginCheck
+    @PutMapping("/info/{id}")
+    public CommonResult<String> handleFeedbackMsg(@RequestHeader("admin_token") String adminToken,
+                                                  @PathVariable("id") Long id) {
+        FeedbackMsgEntity feedbackMsg = feedbackMsgService.getById(id);
+        feedbackMsg.setStatu("2");
+        feedbackMsg.setHandleTime(LocalDateTime.now());
+        feedbackMsgService.updateById(feedbackMsg);
+
+        return CommonResult.ok();
+    }
+}

+ 119 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/PayController.java

@@ -0,0 +1,119 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import com.chuanghai.smartschool.tuitionpayment.common.utils.CommonResult;
+import com.chuanghai.smartschool.tuitionpayment.service.PayService;
+import com.chuanghai.smartschool.tuitionpayment.vo.WechatJsApiPayParamVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 支付接口
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Controller
+@RequestMapping("pay")
+public class PayController {
+
+    @Autowired
+    private PayService payService;
+
+    /**
+     * 【建行】获取拉起微信支付的参数
+     * @param cardNumber 当前登录用户标识
+     * @param orderNo 订单号
+     * @param openId 支付者唯一标识
+     * @return
+     * @throws Exception
+     */
+    @ResponseBody
+    @GetMapping("ccb/getJsApiParam")
+    public CommonResult<WechatJsApiPayParamVO> getCCBJsApiParam(@RequestHeader("card_number")String cardNumber,
+                                                             @RequestParam String orderNo,
+                                                             @RequestParam String openId) {
+        WechatJsApiPayParamVO jsApiPayParam = payService.getCCBJsApiParam(openId, orderNo);
+        return CommonResult.ok().setResult(jsApiPayParam);
+    }
+
+    /**
+     * 【建行】支付结果异步通知
+     * @param request 请求原始数据
+     * @return
+     */
+    @ResponseBody
+    @PostMapping("ccb/notify")
+    public CommonResult<String> ccbNotify(HttpServletRequest request) {
+        payService.ccbNotify(request);
+
+        return CommonResult.ok().setResult("处理成功");
+    }
+
+    /**
+     * 【建行】支付成功回跳页面
+     * @param request
+     * @apiNote 支付成功后,建行服务器会调用该接口
+     *          该接口监听到建行回调后,会以【fontendUrl + pages/paySuccess/paySuccess?orderNo=123】的形式跳回到前端页面
+     *          <strong>注意:</strong>
+     *          fontendUrl + pages/paySuccess/paySuccess是前端支付成功之后的页面,需要前端开发人员与后台开发人员协调好
+     * @return
+     */
+    @GetMapping("ccb/success")
+    public String ccbSuccess(HttpServletRequest request) {
+        String url = payService.ccbSuccess(request);
+
+        return "redirect:" + url;
+    }
+
+    /**
+     * 【农商行】发起支付
+     * @since 0.1.0
+     * @apiNote 页面直接跳转链接【https://q.jxnxs.com/newpay?O=&out_no=&amount=&appoint_notify=】发起支付
+     *          O:固定传5494ec3310685daa218382619dd20e27
+     *          out_no:订单号,下单接口获取
+     *          amount:支付金额,单位元,下单接口获取
+     *          appoint_notify:异步通知地址,测试地址:http://7n9xr5.natappfree.cc/pay/jxnxs/notify、正式地址:https://jtishfw.ncjti.edu.cn/jiaofei/backendApi/pay/jxnxs/notify/
+     * @return
+     */
+    @GetMapping("spec001")
+    public String spec001() {
+        return "";
+    }
+
+    /**
+     * 【农商行】支付结果异步通知
+     * @param request
+     * @since 0.1.0
+     * @return
+     */
+    @ResponseBody
+    @GetMapping("jxnxs/notify")
+    public String jxnxsNotify(HttpServletRequest request) {
+        boolean isSuccess = payService.jxnxsNotify(request);
+        if (isSuccess) {
+            return "notify_success";
+        } else {
+            return "notify_fail";
+        }
+    }
+
+    /**
+     * 【农商行】支付成功跳转地址
+     * @param orderNo 订单号
+     * @since 0.1.0
+     * @return
+     */
+    @GetMapping("jxnxs/success")
+    public String jxnxsSuccess(@RequestParam("ord_no") String orderNo) {
+        String url = payService.jxnxsSuccess(orderNo);
+        return "redirect:" + url;
+    }
+}

+ 94 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/PayMethodSettingController.java

@@ -0,0 +1,94 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import com.baomidou.mybatisplus.annotation.Version;
+import com.chuanghai.smartschool.tuitionpayment.anno.AdminLoginCheck;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.CommonResult;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayMethodSettingEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.PayMethodSettingService;
+import com.chuanghai.smartschool.tuitionpayment.vo.CurrentPayMethodVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * 支付方式设置
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-17 15:52:41
+ * @since 0.1.0
+ */
+@RestController
+@RequestMapping("payMethodSetting")
+public class PayMethodSettingController {
+
+    @Autowired
+    private PayMethodSettingService payMethodSettingService;
+
+    /**
+     * 支付方式列表
+     *
+     * @param adminToken 管理员token
+     * @return
+     */
+    @AdminLoginCheck
+    @GetMapping("/list")
+    public CommonResult<List<PayMethodSettingEntity>> list(@RequestHeader("admin_token") String adminToken){
+        List<PayMethodSettingEntity> list = payMethodSettingService.list();
+        return CommonResult.ok().setResult(list);
+    }
+
+    /**
+     * 设置 payMethodId 为当前支付方式
+     *
+     * @param adminToken 管理员token
+     * @param payMethodId 支付方式id
+     * @return
+     */
+    @AdminLoginCheck
+    @PutMapping("{id}/currentPay")
+    public CommonResult<String> changeCurrentPayMethod(@RequestHeader("admin_token") String adminToken,
+                                                       @PathVariable("id") Long payMethodId) {
+        AtomicBoolean notHandle = new AtomicBoolean(true);
+        List<PayMethodSettingEntity> list = payMethodSettingService.list();
+        list.stream().forEach(payMethod -> {
+            if (payMethod.getId().longValue() == payMethodId.longValue()) {
+                payMethod.setIsCurrentPay("1");
+                notHandle.set(false);
+            } else {
+                payMethod.setIsCurrentPay("2");
+            }
+        });
+
+        if (notHandle.get()) {
+            throw new RRException(BizCodeEnume.DATA_IS_NOT_EXIST, "支付方式id不存在");
+        }
+
+        payMethodSettingService.updateBatchById(list);
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取当前支付方式
+     *
+     * @return
+     */
+    @GetMapping("currentPay")
+    public CommonResult<CurrentPayMethodVO> getCurrentPayMethod() {
+        String currentPayMethod = payMethodSettingService.getCurrentPayMethod();
+        return CommonResult.ok().setResult(CurrentPayMethodVO
+                                                        .builder()
+                                                        .currentPayMethod(currentPayMethod)
+                                                        .build());
+    }
+}

+ 155 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/PayOrderController.java

@@ -0,0 +1,155 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import com.chuanghai.smartschool.tuitionpayment.anno.AdminLoginCheck;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.CommonResult;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayOrderEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.PayOrderService;
+import com.chuanghai.smartschool.tuitionpayment.to.PayOrderQueryTO;
+import com.chuanghai.smartschool.tuitionpayment.vo.CreateOrderResultVO;
+import com.chuanghai.smartschool.tuitionpayment.vo.OrderStatusVO;
+import com.chuanghai.smartschool.tuitionpayment.vo.PayOrderListVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.math.BigDecimal;
+
+/**
+ * 缴费订单 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@RestController
+@RequestMapping("tuitionpayment/payorder")
+public class PayOrderController {
+    @Autowired
+    private PayOrderService payOrderService;
+
+    /**
+     * 创建支付订单
+     * @param cardNumber 当前登录用户cardNumber
+     * @param studentNo 缴费用户studentNo
+     * @return
+     */
+    @PostMapping("/{studentNo}/create")
+    public CommonResult<CreateOrderResultVO> createPayOrder(@RequestHeader("card_number") String cardNumber,
+                                                            @PathVariable("studentNo") String studentNo) {
+
+        CreateOrderResultVO resultVO = payOrderService.createOrder(cardNumber, studentNo);
+
+        return CommonResult.ok().setResult(resultVO);
+    }
+
+    /**
+     * 订单列表【当前用户】
+     * @param cardNumber 当前登录用户cardNumber
+     * @param pageParam 分页参数
+     * @return
+     */
+    @GetMapping("currentUserList")
+    public CommonResult<PageUtils<PayOrderEntity>> currentUserList(@RequestHeader("card_number") String cardNumber,
+                                                                   PageParam pageParam) {
+        PageUtils page = payOrderService.queryPage(pageParam, cardNumber);
+
+        return CommonResult.ok().setResult(page);
+    }
+
+    /**
+     * 查询订单状态【用户端】
+     * @param cardNumber 当前登录用户cardNumber
+     * @param orderNo 订单号
+     * @return
+     */
+    @GetMapping("{orderNo}/orderStatu")
+    public CommonResult<OrderStatusVO> queryOrderStatus(@RequestHeader("card_number") String cardNumber,
+                                                        @PathVariable("orderNo") String orderNo) {
+        OrderStatusVO orderStatusVO = payOrderService.queryOrderStatus(cardNumber, orderNo);
+        return CommonResult.ok().setResult(orderStatusVO);
+    }
+
+    /**
+     * 订单列表【管理端】
+     * @param adminToken 管理员token
+     * @param queryTO 查询条件
+     * @param pageParam 分页参数
+     * @apiNote 0.1.0 新增按照支付方式查询
+     * @return
+     */
+    @AdminLoginCheck
+    @GetMapping("/list")
+    public CommonResult<PayOrderListVO> list(@RequestHeader("admin_token") String adminToken,
+                                                        PayOrderQueryTO queryTO,
+                                                        PageParam pageParam){
+        String flag = queryTO.getFlag();
+        String dateStr = queryTO.getDateStr();
+        if (!("1".equals(flag) || "2".equals(flag) || "3".equals(flag))) {
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, "flag只允许传1、2、3");
+        }
+        if (!"1".equals(flag) && !StringUtils.hasText(dateStr)) {
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, "dateStr不能为空");
+        }
+
+        PageUtils page = payOrderService.queryPage(queryTO, pageParam);
+        // 计算总金额
+        BigDecimal bigDecimal = payOrderService.queryCurrentPayedAmount(queryTO);
+        // 计算当前完成支付记录数量
+        Integer recordNums = payOrderService.queryCurrentPayedRecordNum(queryTO);
+
+        PayOrderListVO payOrderListVO = new PayOrderListVO();
+        payOrderListVO.setTotal(bigDecimal);
+        payOrderListVO.setPage(page);
+        payOrderListVO.setRecordsNum(recordNums);
+
+        return CommonResult.ok().setResult(payOrderListVO);
+    }
+
+    /**
+     * 查询订单状态【管理端】
+     * @param orderNo 订单号
+     * @return
+     */
+    @GetMapping("queryOrderStatu")
+    public CommonResult<OrderStatusVO> queryOrderStatusByAdmin(@RequestHeader("admin_token") String adminToken,
+                                                        @RequestParam String orderNo) {
+        OrderStatusVO orderStatusVO = payOrderService.queryOrderStatus(orderNo);
+        return CommonResult.ok().setResult(orderStatusVO);
+    }
+
+    /**
+     * 下载完成缴费的学生名单
+     * @param adminToken 管理员token
+     * @param flag 1下载所有数据、2下载指定月份数据、3下载指定日期数据
+     * @param dateStr 当flag为2、3时必传,月份格式:yyyy-MM、日期格式:yyyy-MM-dd
+     * @param payMethod 支付方式 不传下载所有,1建行支付、2江西农商行支付
+     * @apiNote 0.1.0新增按照支付方式下载字段
+     * @param response
+     */
+    @GetMapping("downloadResult")
+    @AdminLoginCheck
+    public void downloadResult(@RequestHeader("admin_token") String adminToken,
+                               @RequestParam String flag,
+                               String dateStr,
+                               String payMethod,
+                               HttpServletResponse response) throws IOException {
+        if (!("1".equals(flag) || "2".equals(flag) || "3".equals(flag))) {
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, "flag只允许传1、2、3");
+        }
+        if (!"1".equals(flag) && !StringUtils.hasText(dateStr)) {
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, "dateStr不能为空");
+        }
+        payOrderService.downloadResult(response, flag, dateStr, payMethod);
+    }
+}

+ 175 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/PayableInfoController.java

@@ -0,0 +1,175 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import com.alibaba.excel.EasyExcel;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.chuanghai.smartschool.tuitionpayment.anno.AdminLoginCheck;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.CommonResult;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayableInfoEntity;
+import com.chuanghai.smartschool.tuitionpayment.listener.aliexcel.MyStudentNoImportListener;
+import com.chuanghai.smartschool.tuitionpayment.service.PayOrderService;
+import com.chuanghai.smartschool.tuitionpayment.service.PayableInfoService;
+import com.chuanghai.smartschool.tuitionpayment.to.DeletePayableInfoTO;
+import com.chuanghai.smartschool.tuitionpayment.vo.StudentNoVO;
+import com.chuanghai.smartschool.tuitionpayment.vo.StudentPayableInfoVO;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 学生应缴数据 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@RestController
+@RequestMapping("tuitionpayment/payableinfo")
+public class PayableInfoController {
+
+    @Autowired
+    private PayableInfoService payableInfoService;
+    @Autowired
+    private PayOrderService payOrderService;
+
+    /**
+     * 根据card_number获取缴费信息
+     * @param cardNumber 当前登录用户cardNumber
+     * @return
+     */
+    @GetMapping("payableInfo")
+    public CommonResult<StudentPayableInfoVO> getPayableInfoByCardNumber(@RequestHeader("card_number") String cardNumber) {
+        StudentPayableInfoVO info = payableInfoService.getPayableInfoByCardNumber(cardNumber);
+        return CommonResult.ok().setResult(info);
+    }
+
+    /**
+     * 根据学号和学生姓名获取缴费信息
+     * @param cardNumber 当前登录用户cardNumber
+     * @param studentNo 学号,新生可用身份证号码代替学号
+     * @param studentName 学生姓名
+     * @return
+     */
+    @GetMapping("payableInfo/{studentNo}/{studentName}")
+    public CommonResult<StudentPayableInfoVO> getPayableInfoByCardNumberAndName(@RequestHeader("card_number") String cardNumber,
+                                                                                @PathVariable("studentNo") String studentNo,
+                                                                                @PathVariable("studentName") String studentName) {
+        StudentPayableInfoVO info = payableInfoService.getPayableInfoByStudentNoAndName(studentNo, studentName);
+        return CommonResult.ok().setResult(info);
+    }
+
+    /**
+     * 学生应缴列表
+     * @param adminToken 管理员token
+     * @param keyword 姓名或学号关键字查询
+     * @param pageParam 分页参数
+     * @return
+     */
+    @AdminLoginCheck
+    @GetMapping("/list")
+    public CommonResult<PageUtils<PayableInfoEntity>> list(@RequestHeader("admin_token") String adminToken,
+                                                           String keyword, PageParam pageParam){
+        PageUtils page = payableInfoService.queryPage(keyword, pageParam);
+        return CommonResult.ok().setResult(page);
+    }
+
+    /**
+     * 学生应缴信息删除
+     * @param adminToken 管理员token
+     * @param ids 需要删除记录的id集合
+     * @return
+     */
+    @AdminLoginCheck
+    @DeleteMapping("/delete")
+    public CommonResult<String> delete(@RequestHeader("admin_token") String adminToken,
+                                       @RequestBody Long[] ids){
+
+		payableInfoService.removeByIds(Arrays.asList(ids));
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * excel 导入学生学费相关原始数据
+     * @param adminToken 管理员token
+     * @param file 文件,文件格式注意需要符合要求
+     * @apiNote 导入学生学费相关原始数据
+     *          <strong>注意:</strong>
+     *          1.需要提示用户上传的excel文件需要符合规定的格式;
+     *          2.上传新文件将会导致所有的旧数据被删除,学费缴纳开始后不建议再上传新数据。
+     * @return
+     */
+    @PostMapping("importByExcel")
+    @AdminLoginCheck
+    public CommonResult<String> importByExcel(@RequestHeader("admin_token") String adminToken,
+                                              @RequestParam MultipartFile file) throws IOException {
+        if (file.isEmpty()) {
+            throw new RRException(BizCodeEnume.PARAMETER_ERROR, "文件不能为空");
+        }
+        payableInfoService.importByExcel(file);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除缴费信息
+     * @param adminToken 管理员token
+     * @param file 文件
+     * @since 0.1.0
+     * @apiNote 删除缴费信息
+     * <strong>注意:</strong>
+     * 文件需要符合规定的格式
+     * 不会删除已经在线缴费的学生。如果有没有删除的学生,会在接口返回没有被删除学生的列表。
+     * @return
+     */
+    @DeleteMapping("payableInfo")
+    @AdminLoginCheck
+    public CommonResult<DeletePayableInfoTO> deletePayableInfo(@RequestHeader("admin_token") String adminToken,
+                                                               @RequestParam MultipartFile file) throws IOException {
+
+        MyStudentNoImportListener studentNoImportListener = new MyStudentNoImportListener();
+        EasyExcel.read(file.getInputStream(), StudentNoVO.class, studentNoImportListener).sheet().doRead();
+        List<String> studentNos = studentNoImportListener.getStudentNoList();
+
+        QueryWrapper<PayableInfoEntity> wrapper = new QueryWrapper<>();
+        wrapper.in(studentNos != null && studentNos.size() > 0, "student_no", studentNos);
+        List<PayableInfoEntity> list = payableInfoService.list(wrapper);
+
+        List<DeletePayableInfoTO> alreadyPayList = new ArrayList<>();
+
+        List<Long> collect = list.stream().filter(payableInfo -> {
+            boolean alreadyPay = payOrderService.isAlreadyPay(payableInfo.getStudentNo());
+            if (alreadyPay) {
+                DeletePayableInfoTO to = new DeletePayableInfoTO();
+                BeanUtils.copyProperties(payableInfo, to);
+                to.setMsg("学生已缴费,删除失败!");
+                alreadyPayList.add(to);
+            }
+            return !alreadyPay;
+        }).map(payableInfo -> payableInfo.getId())
+          .collect(Collectors.toList());
+
+        if (collect.size() > 0) {
+            payableInfoService.removeByIds(collect);
+        }
+
+        return CommonResult.ok().setResult(alreadyPayList);
+    }
+
+}

+ 54 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/TestController.java

@@ -0,0 +1,54 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @Author: codingliang
+ * @Description: TODO
+ * @Date: 2021-08-24 18:07
+ * @Version: V1.0
+ **/
+@RestController
+@RequestMapping("test")
+public class TestController {
+
+    @PostMapping("test1")
+    public String test(HttpServletRequest request) throws IOException {
+        StringBuilder stringBuilder = new StringBuilder();
+        BufferedReader bufferedReader = null;
+        try {
+            InputStream inputStream = request.getInputStream();
+            if (inputStream != null) {
+                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
+                char[] charBuffer = new char[128];
+                int bytesRead;
+                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
+                    stringBuilder.append(charBuffer, 0, bytesRead);
+                }
+            }
+        } catch (IOException ex) {
+            throw ex;
+        } finally {
+            if (bufferedReader != null) {
+                try {
+                    bufferedReader.close();
+                } catch (IOException ex) {
+                    throw ex;
+                }
+            }
+        }
+        System.out.println("body:" + stringBuilder.toString());
+
+        return "code=1111";
+    }
+}

+ 119 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/controller/WechatController.java

@@ -0,0 +1,119 @@
+package com.chuanghai.smartschool.tuitionpayment.controller;
+
+import com.chuanghai.smartschool.tuitionpayment.config.WechatConfig;
+import com.chuanghai.smartschool.tuitionpayment.config.WeixiaoConfig;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.client.RestTemplate;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信相关接口
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 16:04
+ */
+@Controller
+@RequestMapping("wechat")
+public class WechatController {
+
+    @Autowired
+    private WechatConfig wechatConfig;
+    @Autowired
+    private WeixiaoConfig weixiaoConfig;
+
+    /**
+     * 微信授权回调地址
+     *
+     * @apiNote 用户跳转到微信授权链接上,微信服务器接收用户授权后会携带code回调到该接口上,该接口使用code获取到用户的openId信息。
+     *          如果openId获取成功,该接口会以 【fontendUrl + 路由地址 + ?openId=wx123456】 的形式跳回到前端应用页面;
+     *          如果openId获取失败,该接口会以 【fontendUrl + /error/?errorMsg=获取用户openId失败】 的形式跳回到前端应用页面。
+     *          <strong>注意:</strong>
+     *          fontendUrl + 路由地址:前端项目地址+页面路由地址,用于前端页面接收用户的信息或错误信息,该地址需要前端人员与后台开发人员协调好。
+     * @param code 微信code
+     */
+    @GetMapping("pub/auth")
+    public String pubAuth(String code, String state) {
+
+        // code换取access_token
+        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
+                "appid=" + wechatConfig.getAppid() +
+                "&secret=" + wechatConfig.getSecret() +
+                "&code=" + code +
+                "&grant_type=authorization_code";
+
+        try {
+            RestTemplate restTemplate = new RestTemplate();
+            restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 解决回参中文乱码
+            String response = restTemplate.getForObject(url, String.class);
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode jsonNode = mapper.readTree(response);
+            String openid = jsonNode.get("openid").asText();
+
+            System.out.println("redirect:" + wechatConfig.getFontendUrl() + "/#/pages/Pay/pay/?openId=" + openid);
+            return "redirect:" + wechatConfig.getFontendUrl() + "/#/" + state + "/?openId=" + openid;
+        } catch (Exception e) {
+            return "redirect:" + wechatConfig.getFontendUrl() + "/error/?errorMsg=获取用户openId失败";
+        }
+    }
+
+    /**
+     * 微校授权回调地址
+     *
+     * @apiNote 用户跳转到微校授权链接上,微校服务器接收用户授权后会携带wxcode回调到该接口上,该接口使用wxcode获取到用户的信息。
+     *          如果用户信息获取成功,该接口会以 【fontendUrl + /?cardNumber=wx123456&name=测试用户】 的形式跳回到前端应用页面;
+     *          如果用户信息获取失败,该接口会以 【fontendUrl + /error/?errorMsg=获取用户信息失败】 的形式跳回到前端应用页面。
+     *          <strong>注意:</strong>
+     *          fontendUrl + 路由地址:前端项目地址+页面路由地址,用于前端页面接收用户的信息或错误信息,该地址需要前端人员与后台开发人员协调好。
+     * </p>
+     * @param wxcode 微校code
+     * @param state 透传数据
+     * @return
+     */
+    @GetMapping("weixiao/auth")
+    public String weixiaoAuth(String wxcode, String state) {
+        String tokenUrl = "https://open.wecard.qq.com/connect/oauth2/token";
+        Map<String, String> tokenParams = new HashMap<>();
+        tokenParams.put("wxcode", wxcode);
+        tokenParams.put("app_key", weixiaoConfig.getAppKey());
+        tokenParams.put("app_secret", weixiaoConfig.getAppSecret());
+        tokenParams.put("grant_type", "authorization_code");
+        tokenParams.put("redirect_uri", state); // 该地址需要与发起授权地址保持一直
+
+        try {
+            // wecode换取token
+            RestTemplate client = new RestTemplate();
+            ResponseEntity<String> tokenResponse = client.postForEntity(tokenUrl, tokenParams, String.class);
+
+            ObjectMapper mapper = new ObjectMapper();
+            Map<String, Object> jsonMap = mapper.readValue(tokenResponse.getBody(), new TypeReference<Map<String, Object>>() {});
+            String accessToken = (String) jsonMap.get("access_token");
+
+            // token换取用户信息
+            String userInfoUrl = "https://open.wecard.qq.com/connect/oauth/get-user-info";
+            Map<String, String> userInfoParam = new HashMap<>();
+            userInfoParam.put("access_token", accessToken);
+            ResponseEntity<String> userInfoResponse = client.postForEntity(userInfoUrl, userInfoParam, String.class);
+
+            Map<String, Object> userInfoMap = mapper.readValue(userInfoResponse.getBody(), new TypeReference<Map<String, Object>>() {});
+            String cardNumber = userInfoMap.get("card_number").toString();
+            String encodeName = URLEncoder.encode(userInfoMap.get("name").toString(), "utf-8");
+
+            return "redirect:" + weixiaoConfig.getFontendUrl() + "/#/pages/blankIndex/blankIndex/?cardNumber=" + cardNumber + "&name=" + encodeName;
+        } catch (Exception e) {
+            return "redirect:" + weixiaoConfig.getFontendUrl() + "/#/error/";
+        }
+    }
+}

+ 44 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/conver/aliexcel/CustomPayMethodConverter.java

@@ -0,0 +1,44 @@
+package com.chuanghai.smartschool.tuitionpayment.conver.aliexcel;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.CellData;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import com.chuanghai.smartschool.tuitionpayment.enume.PayMethod;
+
+/**
+ * @Author: codingliang
+ * @Description: 支付方式转化器
+ * @Date: 2021-08-24 15:00
+ * @Version: V1.0
+ **/
+public class CustomPayMethodConverter implements Converter<String> {
+    @Override
+    public Class supportJavaTypeKey() {
+        return String.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        return cellData.getStringValue();
+    }
+
+    @Override
+    public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        String payMethod;
+        if (PayMethod.CCB_PAY.getCode().equals(value)) {
+            payMethod = PayMethod.CCB_PAY.getMsg();
+        } else if (PayMethod.JXNXS_PAY.getCode().equals(value)) {
+            payMethod = PayMethod.JXNXS_PAY.getMsg();
+        } else {
+            payMethod = "未知支付方式";
+        }
+        return new CellData<>(payMethod);
+    }
+}

+ 38 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/conver/aliexcel/LocalDateTimeConverter.java

@@ -0,0 +1,38 @@
+package com.chuanghai.smartschool.tuitionpayment.conver.aliexcel;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.CellData;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @Author: codingliang
+ * @Description: LocalDateTime 转发器
+ * @Date: 2021-08-13 12:38
+ * @Version: V1.0
+ **/
+public class LocalDateTimeConverter implements Converter<LocalDateTime> {
+    @Override
+    public Class supportJavaTypeKey() {
+        return LocalDateTime.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+    }
+
+    @Override
+    public CellData convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
+        return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+    }
+}

+ 17 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/AdminInfoDao.java

@@ -0,0 +1,17 @@
+package com.chuanghai.smartschool.tuitionpayment.dao;
+
+import com.chuanghai.smartschool.tuitionpayment.entity.AdminInfoEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 管理员信息 
+ * 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Mapper
+public interface AdminInfoDao extends BaseMapper<AdminInfoEntity> {
+	
+}

+ 16 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/EnablePayLimitByGradeDao.java

@@ -0,0 +1,16 @@
+package com.chuanghai.smartschool.tuitionpayment.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chuanghai.smartschool.tuitionpayment.entity.EnablePayLimitByGradeEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 年级可支付限制
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Mapper
+public interface EnablePayLimitByGradeDao extends BaseMapper<EnablePayLimitByGradeEntity> {
+}

+ 17 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/FeedbackMsgDao.java

@@ -0,0 +1,17 @@
+package com.chuanghai.smartschool.tuitionpayment.dao;
+
+import com.chuanghai.smartschool.tuitionpayment.entity.FeedbackMsgEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 反馈信息表 
+ * 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Mapper
+public interface FeedbackMsgDao extends BaseMapper<FeedbackMsgEntity> {
+	
+}

+ 9 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/PayMethodSettingDao.java

@@ -0,0 +1,9 @@
+package com.chuanghai.smartschool.tuitionpayment.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayMethodSettingEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PayMethodSettingDao extends BaseMapper<PayMethodSettingEntity> {
+}

+ 17 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/PayOrderDao.java

@@ -0,0 +1,17 @@
+package com.chuanghai.smartschool.tuitionpayment.dao;
+
+import com.chuanghai.smartschool.tuitionpayment.entity.PayOrderEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 缴费订单 
+ * 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Mapper
+public interface PayOrderDao extends BaseMapper<PayOrderEntity> {
+	
+}

+ 17 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/dao/PayableInfoDao.java

@@ -0,0 +1,17 @@
+package com.chuanghai.smartschool.tuitionpayment.dao;
+
+import com.chuanghai.smartschool.tuitionpayment.entity.PayableInfoEntity;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生应缴数据 
+ * 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Mapper
+public interface PayableInfoDao extends BaseMapper<PayableInfoEntity> {
+    void truncateTable();
+}

+ 54 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/AdminInfoEntity.java

@@ -0,0 +1,54 @@
+package com.chuanghai.smartschool.tuitionpayment.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+
+/**
+ * 管理员信息 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Data
+@TableName("admin_info")
+public class AdminInfoEntity implements Serializable {
+	private static final long serialVersionUID = -8936652337177903377L;
+
+	/**
+	 * 管理员id,新增时不用传
+	 */
+	@TableId
+	private Long id;
+	/**
+	 * 用户名
+	 */
+	@NotBlank(message = "用户名不能为空")
+	private String userName;
+	/**
+	 * 登录密码, 新增时不能为空
+	 */
+	@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+	private String password;
+	/**
+	 * 管理员类型 0超级管理员、1其他管理员
+	 */
+	@NotBlank(message = "用户类别不能为空")
+	@Pattern(regexp = "^[1-9]*$", message = "管理员类别不能为0")
+	private String adminType;
+	/**
+	 * 管理员状态 0冻结、1正常
+	 */
+	@NotBlank(message = "状态不能为空")
+	@Pattern(regexp = "^[0-1]*$", message = "状态只能为0或1")
+	private String status;
+
+}

+ 31 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/EnablePayLimitByGradeEntity.java

@@ -0,0 +1,31 @@
+package com.chuanghai.smartschool.tuitionpayment.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * 年级可支付限制
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Data
+@TableName("enable_pay_limit_by_grade")
+public class EnablePayLimitByGradeEntity {
+
+    @TableId
+    private Long id;
+    /**
+     * 年级名称
+     */
+    private String gradeName;
+    /**
+     * 年级
+     */
+    private String grade;
+    /**
+     * 是否可支付 1能、2不能
+     */
+    private String enablePay;
+}

+ 64 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/FeedbackMsgEntity.java

@@ -0,0 +1,64 @@
+package com.chuanghai.smartschool.tuitionpayment.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import net.sf.cglib.core.Local;
+
+/**
+ * 反馈信息表 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Data
+@TableName("feedback_msg")
+public class FeedbackMsgEntity implements Serializable {
+	private static final long serialVersionUID = 1540570041000020629L;
+
+	/**
+	 * id
+	 */
+	@TableId
+	private Long id;
+	/**
+	 * 反馈者身份标识 使用用户的card_number标识
+	 */
+	private String feedbackPersonIdentify;
+	/**
+	 * 反馈者姓名
+	 */
+	private String feedbackPersonName;
+	/**
+	 * 反馈者电话
+	 */
+	private String feedbackPersonPhone;
+	/**
+	 * 反馈问题
+	 */
+	private String feedbackInfo;
+	/**
+	 * 反馈状态 1未处理、2已处理
+	 */
+	private String statu;
+
+	/**
+	 * 反馈发布时间
+	 */
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	private LocalDateTime publishTime;
+
+	/**
+	 * 反馈处理时间
+	 */
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	private LocalDateTime handleTime;
+
+}

+ 44 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/PayMethodSettingEntity.java

@@ -0,0 +1,44 @@
+package com.chuanghai.smartschool.tuitionpayment.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 支付方式设置
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Data
+@TableName("pay_method_setting")
+public class PayMethodSettingEntity implements Serializable {
+
+    private static final long serialVersionUID = -6744523575143914281L;
+
+    /**
+     * 支付方式id
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 支付方式名称
+     */
+    private String payMethodName;
+
+    /**
+     * 支付方式 1建行支付、2农商行支付
+     */
+    private String payMethod;
+
+    /**
+     * 是否为当前支付方式 1是,2否
+     */
+    @JsonProperty("currentPay")
+    private String isCurrentPay;
+}

+ 90 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/PayOrderEntity.java

@@ -0,0 +1,90 @@
+package com.chuanghai.smartschool.tuitionpayment.entity;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.chuanghai.smartschool.tuitionpayment.conver.aliexcel.CustomPayMethodConverter;
+import com.chuanghai.smartschool.tuitionpayment.conver.aliexcel.LocalDateTimeConverter;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 缴费订单 
+ * 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Data
+@TableName("pay_order")
+public class PayOrderEntity implements Serializable {
+	private static final long serialVersionUID = -3368168683789891484L;
+
+	/**
+	 * id
+	 */
+	@TableId
+	@ExcelIgnore
+	private Long id;
+	/**
+	 * 订单号
+	 */
+	@ExcelProperty("订单号")
+	private String orderNo;
+	/**
+	 * 付款者id 付款人的card_number
+	 */
+	@ExcelIgnore
+	private String payerIdentify;
+	/**
+	 * 学生id 被付款人的card_number
+	 */
+	@ExcelProperty("学号")
+	private String payForIdentify;
+	/**
+	 * 学生姓名
+	 */
+	@ExcelProperty("姓名")
+	private String studentName;
+	/**
+	 * 学生班级名称
+	 */
+	@ExcelProperty("班级名称")
+	private String className;
+	/**
+	 * 订单金额
+	 */
+	@ExcelProperty("支付金额")
+	private BigDecimal orderAmount;
+	/**
+	 * 订单状态 1未支付、2支付成功
+	 */
+	@ExcelIgnore
+	private String status;
+
+	/**
+	 * 支付方式 1建行支付、2农商行支付
+	 * @since 0.1.0
+	 */
+	@ExcelProperty(value = "支付方式", converter= CustomPayMethodConverter.class)
+	private String payMethod;
+
+	/**
+	 * 创建时间
+	 */
+	@ExcelIgnore
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	private LocalDateTime createTime;
+	/**
+	 * 完成时间
+	 */
+	@ExcelProperty(value = "支付完成时间", converter = LocalDateTimeConverter.class)
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	private LocalDateTime finishTime;
+
+}

+ 75 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/entity/PayableInfoEntity.java

@@ -0,0 +1,75 @@
+package com.chuanghai.smartschool.tuitionpayment.entity;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.math.BigDecimal;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 学生应缴数据 
+ * 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+@Data
+@TableName("payable_info")
+public class PayableInfoEntity implements Serializable {
+	private static final long serialVersionUID = -8945955742877200423L;
+
+	/**
+	 * 应缴信息id
+	 */
+	@TableId
+	private Long id;
+	/**
+	 * 学号
+	 */
+	@ExcelProperty("学号")
+	private String studentNo;
+	/**
+	 * 姓名
+	 */
+	@ExcelProperty("姓名")
+	private String studentName;
+	/**
+	 * 年级
+	 */
+	@ExcelProperty("年级")
+	private String grade;
+	/**
+	 * 收费年度
+	 */
+	@ExcelProperty("收费年度")
+	private String years;
+	/**
+	 * 学院名称
+	 */
+	@ExcelProperty("学院名称")
+	private String collegeName;
+	/**
+	 * 专业名称
+	 */
+	@ExcelProperty("专业名称")
+	private String majorName;
+	/**
+	 * 班级名称
+	 */
+	@ExcelProperty("班级名称")
+	private String className;
+	/**
+	 * 应付金额
+	 */
+	@ExcelProperty("应付金额")
+	private BigDecimal realPayAmount;
+	/**
+	 * 应付详情 格式:教材费:1000\学费:18000\住宿费:1800
+	 */
+	@ExcelProperty("缴费详情")
+	private String payItemDetail;
+
+}

+ 22 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/enume/FeedbackMsgStatus.java

@@ -0,0 +1,22 @@
+package com.chuanghai.smartschool.tuitionpayment.enume;
+
+public enum  FeedbackMsgStatus {
+
+    ALREADY_HANDLE("2", "已处理"),
+    NON_HANDLE("1", "未处理");
+
+    private String code;
+    private String msg;
+    FeedbackMsgStatus(String code,String msg){
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 22 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/enume/PayMethod.java

@@ -0,0 +1,22 @@
+package com.chuanghai.smartschool.tuitionpayment.enume;
+
+public enum PayMethod {
+
+    CCB_PAY("1", "建行支付"),
+    JXNXS_PAY("2", "江西农商行支付");
+
+    private String code;
+    private String msg;
+    PayMethod(String code,String msg){
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 28 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/enume/PayStatus.java

@@ -0,0 +1,28 @@
+package com.chuanghai.smartschool.tuitionpayment.enume;
+
+/**
+ * @Author: codingliang
+ * @Description: 支付状态
+ * @Date: 2021-08-12 16:58
+ * @Version: V1.0
+ **/
+public enum PayStatus {
+
+    ALREADY_PAY("2", "已支付"),
+    NON_PAY("1", "未支付");
+
+    private String code;
+    private String msg;
+    PayStatus(String code,String msg){
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 47 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/listener/aliexcel/MyPayableInfoImportListener.java

@@ -0,0 +1,47 @@
+package com.chuanghai.smartschool.tuitionpayment.listener.aliexcel;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayableInfoEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.PayableInfoService;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author: codingliang
+ * @Description: 学生应缴数据导入
+ * @Date: 2021-08-13 11:46
+ * @Version: V1.0
+ **/
+public class MyPayableInfoImportListener extends AnalysisEventListener<PayableInfoEntity> {
+
+    private static final int BATCH_COUNT = 100;
+    private List<PayableInfoEntity> list = new ArrayList<>();
+    private PayableInfoService payableInfoService;
+
+    public MyPayableInfoImportListener(PayableInfoService payableInfoService) {
+        this.payableInfoService = payableInfoService;
+    }
+
+    @Transactional
+    @Override
+    public void invoke(PayableInfoEntity data, AnalysisContext context) {
+        list.add(data);
+        if (list.size() >= BATCH_COUNT) {
+            inertData();
+        }
+    }
+
+    @Transactional
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+        inertData();
+    }
+
+    private void inertData() {
+        payableInfoService.saveBatch(list);
+        list.clear();
+    }
+}

+ 32 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/listener/aliexcel/MyStudentNoImportListener.java

@@ -0,0 +1,32 @@
+package com.chuanghai.smartschool.tuitionpayment.listener.aliexcel;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.chuanghai.smartschool.tuitionpayment.to.DeletePayableInfoTO;
+import com.chuanghai.smartschool.tuitionpayment.vo.StudentNoVO;
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author: codingliang
+ * @Description: 学生学号导入
+ * @Date: 2021-08-20 11:45
+ * @Version: V1.0
+ **/
+public class MyStudentNoImportListener extends AnalysisEventListener<StudentNoVO> {
+
+    @Getter
+    private List<String> studentNoList = new ArrayList<>();
+
+
+    @Override
+    public void invoke(StudentNoVO studentNo, AnalysisContext context) {
+        studentNoList.add(studentNo.getStudentNo());
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+    }
+}

+ 22 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/AdminInfoService.java

@@ -0,0 +1,22 @@
+package com.chuanghai.smartschool.tuitionpayment.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.entity.AdminInfoEntity;
+import com.chuanghai.smartschool.tuitionpayment.vo.UserTokenVO;
+
+/**
+ * 管理员信息 
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+public interface AdminInfoService extends IService<AdminInfoEntity> {
+
+    PageUtils queryPage(PageParam pageParam);
+
+    UserTokenVO login(String userName, String password);
+}
+

+ 16 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/EnablePayLimitByGradeService.java

@@ -0,0 +1,16 @@
+package com.chuanghai.smartschool.tuitionpayment.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.smartschool.tuitionpayment.entity.EnablePayLimitByGradeEntity;
+
+/**
+ * 年级可支付限制
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+public interface EnablePayLimitByGradeService extends IService<EnablePayLimitByGradeEntity> {
+
+    boolean getCurrentGradeEnablePay(Integer grade);
+}

+ 24 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/FeedbackMsgService.java

@@ -0,0 +1,24 @@
+package com.chuanghai.smartschool.tuitionpayment.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.entity.FeedbackMsgEntity;
+import com.chuanghai.smartschool.tuitionpayment.to.FeedbackMsgCreateTO;
+
+/**
+ * 反馈信息表 
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+public interface FeedbackMsgService extends IService<FeedbackMsgEntity> {
+
+    PageUtils queryPageByStatus(String status, String keyword, PageParam pageParam);
+
+    PageUtils queryPage(String cardNumber, PageParam pageParam);
+
+    void save(String cardNumber, FeedbackMsgCreateTO feedbackMsgCreateTO);
+}
+

+ 9 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/PayMethodSettingService.java

@@ -0,0 +1,9 @@
+package com.chuanghai.smartschool.tuitionpayment.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayMethodSettingEntity;
+
+public interface PayMethodSettingService extends IService<PayMethodSettingEntity> {
+
+    String getCurrentPayMethod();
+}

+ 50 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/PayOrderService.java

@@ -0,0 +1,50 @@
+package com.chuanghai.smartschool.tuitionpayment.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayOrderEntity;
+import com.chuanghai.smartschool.tuitionpayment.to.PayOrderQueryTO;
+import com.chuanghai.smartschool.tuitionpayment.vo.CreateOrderResultVO;
+import com.chuanghai.smartschool.tuitionpayment.vo.OrderStatusVO;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.math.BigDecimal;
+
+/**
+ * 缴费订单 
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+public interface PayOrderService extends IService<PayOrderEntity> {
+
+    PageUtils queryPage(PayOrderQueryTO queryTO, PageParam pageParam);
+
+    PageUtils queryPage(PageParam pageParam, String cardNumber);
+
+    /**
+     * 获取用户是否缴费
+     * @param studentNo 学号
+     * @return
+     */
+    boolean isAlreadyPay(String studentNo);
+
+    CreateOrderResultVO createOrder(String cardNumber, String studentNo);
+
+    PayOrderEntity getByOrderNo(String orderNo);
+
+    OrderStatusVO queryOrderStatus(String cardNumber, String orderNo);
+
+    OrderStatusVO queryOrderStatus(String orderNo);
+
+    OrderStatusVO queryOrderStatus(PayOrderEntity payOrder);
+
+    BigDecimal queryCurrentPayedAmount(PayOrderQueryTO queryTO);
+
+    void downloadResult(HttpServletResponse response, String flag, String dateStr, String payMethod) throws IOException;
+
+    Integer queryCurrentPayedRecordNum(PayOrderQueryTO queryTO);
+}
+

+ 23 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/PayService.java

@@ -0,0 +1,23 @@
+package com.chuanghai.smartschool.tuitionpayment.service;
+
+import com.chuanghai.smartschool.tuitionpayment.vo.WechatJsApiPayParamVO;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+public interface PayService {
+
+    WechatJsApiPayParamVO getCCBJsApiParam(String openId, String orderNo);
+
+    void ccbNotify(HttpServletRequest request);
+
+    Map<String, String> queryCCBOrderStatus(String orderNo);
+
+    String ccbSuccess(HttpServletRequest request);
+
+    boolean jxnxsNotify(HttpServletRequest request);
+
+    String jxnxsSuccess(String orderNo);
+
+    Map<String, String> queryJXNXSOrderStatus(String orderNo);
+}

+ 33 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/PayableInfoService.java

@@ -0,0 +1,33 @@
+package com.chuanghai.smartschool.tuitionpayment.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayableInfoEntity;
+import com.chuanghai.smartschool.tuitionpayment.vo.StudentPayableInfoVO;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+
+/**
+ * 学生应缴数据 
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2021-08-12 15:52:41
+ */
+public interface PayableInfoService extends IService<PayableInfoEntity> {
+
+    PageUtils queryPage(String keyword, PageParam pageParam);
+
+    StudentPayableInfoVO getPayableInfoByCardNumber(String cardNumber);
+
+    StudentPayableInfoVO getPayableInfoByStudentNoAndName(String studentNo, String studentName);
+
+    PayableInfoEntity getByStudentNo(String studentNo);
+
+    void importByExcel(MultipartFile file) throws IOException;
+
+    void truncateTable();
+}
+

+ 68 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/AdminInfoServiceImpl.java

@@ -0,0 +1,68 @@
+package com.chuanghai.smartschool.tuitionpayment.service.impl;
+
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.JwtOperator;
+import com.chuanghai.smartschool.tuitionpayment.vo.UserTokenVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.MyQuery;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+
+import com.chuanghai.smartschool.tuitionpayment.dao.AdminInfoDao;
+import com.chuanghai.smartschool.tuitionpayment.entity.AdminInfoEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.AdminInfoService;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.Map;
+
+
+@Service("adminInfoService")
+public class AdminInfoServiceImpl extends ServiceImpl<AdminInfoDao, AdminInfoEntity> implements AdminInfoService {
+
+    @Autowired
+    private JwtOperator jwtOperator;
+
+    @Override
+    public PageUtils queryPage(PageParam pageParam) {
+        IPage<AdminInfoEntity> page = this.page(
+                new MyQuery<AdminInfoEntity>().getPage(pageParam),
+                new QueryWrapper<>()
+        );
+
+        return new PageUtils(page);
+    }
+
+    @Override
+    public UserTokenVO login(String userName, String password) {
+        QueryWrapper<AdminInfoEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("user_name", userName);
+        AdminInfoEntity entity = this.getOne(wrapper);
+        if (entity == null) {
+            throw new RRException(BizCodeEnume.ADMIN_LOGIN_FAIL);
+        }
+
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        if (!encoder.matches(password, entity.getPassword())) {
+            throw new RRException(BizCodeEnume.ADMIN_LOGIN_FAIL);
+        }
+
+        // 登录成功,生成token
+        Map<String, Object> map = new HashMap<>();
+        map.put("userId", entity.getId());
+        String token = jwtOperator.generateToken(map);
+
+        return UserTokenVO.builder()
+                .userName(userName)
+                .token(token)
+                .expireTime(LocalDateTime.now().plusSeconds(jwtOperator.getExpirationTimeInSecond() - 30).toInstant(ZoneOffset.of("+8")).toEpochMilli())
+                .build();
+    }
+}

+ 31 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/EnablePayLimitByGradeServiceImpl.java

@@ -0,0 +1,31 @@
+package com.chuanghai.smartschool.tuitionpayment.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.dao.EnablePayLimitByGradeDao;
+import com.chuanghai.smartschool.tuitionpayment.entity.EnablePayLimitByGradeEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.EnablePayLimitByGradeService;
+import org.springframework.stereotype.Service;
+
+@Service("enablePayLimitByGradeService")
+public class EnablePayLimitByGradeServiceImpl extends ServiceImpl<EnablePayLimitByGradeDao, EnablePayLimitByGradeEntity> implements EnablePayLimitByGradeService {
+
+    /**
+     * 当前年级是否可缴费
+     * @param grade 1 =》 表示大一
+     * @return
+     */
+    public boolean getCurrentGradeEnablePay(Integer grade) {
+        QueryWrapper<EnablePayLimitByGradeEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("grade", grade);
+
+        EnablePayLimitByGradeEntity entity = this.getOne(wrapper);
+        if (entity == null) {
+            throw new RRException(BizCodeEnume.PAY_GRADE_IS_NOT_EXIT);
+        }
+
+        return "1".equals(entity.getEnablePay())? true : false;
+    }
+}

+ 71 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/FeedbackMsgServiceImpl.java

@@ -0,0 +1,71 @@
+package com.chuanghai.smartschool.tuitionpayment.service.impl;
+
+import com.chuanghai.smartschool.tuitionpayment.enume.FeedbackMsgStatus;
+import com.chuanghai.smartschool.tuitionpayment.to.FeedbackMsgCreateTO;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.MyQuery;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+
+import com.chuanghai.smartschool.tuitionpayment.dao.FeedbackMsgDao;
+import com.chuanghai.smartschool.tuitionpayment.entity.FeedbackMsgEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.FeedbackMsgService;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+
+
+@Service("feedbackMsgService")
+public class FeedbackMsgServiceImpl extends ServiceImpl<FeedbackMsgDao, FeedbackMsgEntity> implements FeedbackMsgService {
+
+    @Override
+    public PageUtils queryPageByStatus(String status, String keyword, PageParam pageParam) {
+        QueryWrapper<FeedbackMsgEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq(StringUtils.hasText(status), "statu", status);
+
+        if (StringUtils.hasText(keyword)) {
+            wrapper.and(w -> w.like("feedback_person_name", keyword)
+                    .or()
+                    .like("feedback_person_phone", keyword));
+        }
+
+        IPage<FeedbackMsgEntity> page = this.page(
+                new MyQuery<FeedbackMsgEntity>().getPage(pageParam),
+                wrapper
+        );
+
+        return new PageUtils(page);
+    }
+
+    @Override
+    public PageUtils queryPage(String cardNumber, PageParam pageParam) {
+        IPage<FeedbackMsgEntity> page = this.page(
+                new MyQuery<FeedbackMsgEntity>().getPage(pageParam),
+                new QueryWrapper<FeedbackMsgEntity>().eq("feedback_person_identify", cardNumber)
+        );
+
+        return new PageUtils(page);
+    }
+
+    /**
+     * 新增反馈信息
+     * @param cardNumber 反馈者id
+     * @param feedbackMsgCreateTO 反馈信息to
+     */
+    @Override
+    public void save(String cardNumber, FeedbackMsgCreateTO feedbackMsgCreateTO) {
+        FeedbackMsgEntity feedbackMsgEntity = new FeedbackMsgEntity();
+
+        BeanUtils.copyProperties(feedbackMsgCreateTO, feedbackMsgEntity);
+
+        feedbackMsgEntity.setFeedbackPersonIdentify(cardNumber);
+        feedbackMsgEntity.setPublishTime(LocalDateTime.now());
+        feedbackMsgEntity.setStatu(FeedbackMsgStatus.NON_HANDLE.getCode());
+        this.save(feedbackMsgEntity);
+    }
+
+}

+ 30 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/PayMethodSettingServiceImpl.java

@@ -0,0 +1,30 @@
+package com.chuanghai.smartschool.tuitionpayment.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.smartschool.tuitionpayment.dao.PayMethodSettingDao;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayMethodSettingEntity;
+import com.chuanghai.smartschool.tuitionpayment.enume.PayMethod;
+import com.chuanghai.smartschool.tuitionpayment.service.PayMethodSettingService;
+import org.springframework.stereotype.Service;
+
+
+@Service("payMethodSettingService")
+public class PayMethodSettingServiceImpl extends ServiceImpl<PayMethodSettingDao, PayMethodSettingEntity> implements PayMethodSettingService {
+
+    /**
+     * 获取当前支付方式
+     * @return 1建行支付、2农商行支付
+     */
+    @Override
+    public String getCurrentPayMethod() {
+        QueryWrapper<PayMethodSettingEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("is_current_pay", "1");
+        PayMethodSettingEntity methodSettingEntity = this.getOne(wrapper);
+        if (methodSettingEntity == null) {
+            return PayMethod.CCB_PAY.getCode();
+        }
+
+        return methodSettingEntity.getPayMethod();
+    }
+}

+ 369 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/PayOrderServiceImpl.java

@@ -0,0 +1,369 @@
+package com.chuanghai.smartschool.tuitionpayment.service.impl;
+
+import com.alibaba.excel.EasyExcel;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.MyQuery;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.dao.PayOrderDao;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayOrderEntity;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayableInfoEntity;
+import com.chuanghai.smartschool.tuitionpayment.enume.PayMethod;
+import com.chuanghai.smartschool.tuitionpayment.enume.PayStatus;
+import com.chuanghai.smartschool.tuitionpayment.service.EnablePayLimitByGradeService;
+import com.chuanghai.smartschool.tuitionpayment.service.PayMethodSettingService;
+import com.chuanghai.smartschool.tuitionpayment.service.PayOrderService;
+import com.chuanghai.smartschool.tuitionpayment.service.PayService;
+import com.chuanghai.smartschool.tuitionpayment.service.PayableInfoService;
+import com.chuanghai.smartschool.tuitionpayment.to.PayOrderQueryTO;
+import com.chuanghai.smartschool.tuitionpayment.utils.MyUtil;
+import com.chuanghai.smartschool.tuitionpayment.vo.CreateOrderResultVO;
+import com.chuanghai.smartschool.tuitionpayment.vo.OrderStatusVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Service("payOrderService")
+public class PayOrderServiceImpl extends ServiceImpl<PayOrderDao, PayOrderEntity> implements PayOrderService {
+
+    @Autowired
+    private PayableInfoService payableInfoService;
+    @Autowired
+    private PayMethodSettingService payMethodSettingService;
+    @Autowired
+    private EnablePayLimitByGradeService enablePayLimitByGradeService;
+    @Autowired
+    private PayService payService;
+
+    @Override
+    public PageUtils queryPage(PayOrderQueryTO queryTO, PageParam pageParam) {
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+
+        commonWrapper(wrapper, queryTO);
+
+        wrapper.orderByDesc("finish_time").orderByDesc("create_time").orderByAsc("pay_for_identify");
+
+        IPage<PayOrderEntity> page = this.page(
+                new MyQuery<PayOrderEntity>().getPage(pageParam),
+                wrapper
+        );
+
+        return new PageUtils(page);
+    }
+
+    @Override
+    public PageUtils queryPage(PageParam pageParam, String cardNumber) {
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("payer_identify", cardNumber);
+        IPage<PayOrderEntity> page = this.page(
+                new MyQuery<PayOrderEntity>().getPage(pageParam),
+                wrapper
+        );
+
+        return new PageUtils(page);
+    }
+
+    /**
+     * 获取用户是否缴费
+     * @param studentNo 学号
+     * @return
+     */
+    @Override
+    public boolean isAlreadyPay(String studentNo) {
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("pay_for_identify", studentNo);
+        wrapper.eq("status", PayStatus.ALREADY_PAY.getCode());
+
+        List<PayOrderEntity> payList = this.list(wrapper);
+        if (payList != null && payList.size() > 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 创建缴费订单
+     * @param cardNumber 当前用户
+     * @param studentNo 需要缴费的用户
+     * @return
+     */
+    @Override
+    public CreateOrderResultVO createOrder(String cardNumber, String studentNo) {
+        // 查询用户是否已经缴费
+        boolean alreadyPay = isAlreadyPay(studentNo);
+        if (alreadyPay) {
+            throw new RRException(BizCodeEnume.DATA_IS_EXIST, "学号【" + studentNo + "】已缴费,请勿重复缴费");
+        }
+
+        // 查询支付金额、被支付用户信息
+        PayableInfoEntity payableInfo = payableInfoService.getByStudentNo(studentNo);
+
+        // 获取学生入学年份
+        String grade = payableInfo.getGrade();
+        Integer currentGrade = MyUtil.calcStudentGrade(Integer.valueOf(grade.trim()));
+
+        // 判断当前年级是否可线上缴费
+        boolean enablePay = enablePayLimitByGradeService.getCurrentGradeEnablePay(currentGrade);
+        if (!enablePay) {
+            throw new RRException(BizCodeEnume.PAYABLE_INFO_IS_NOT_EXIT, "当前学生所在年级不允许线上缴费");
+        }
+
+        // 获取当前支付方式
+        String currentPayMethod = payMethodSettingService.getCurrentPayMethod();
+
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("payer_identify", cardNumber);
+        wrapper.eq("pay_for_identify", studentNo);
+        wrapper.orderByDesc("create_time");
+        wrapper.last("limit 1");
+        PayOrderEntity payOrderEntity = this.getOne(wrapper);
+
+
+        // 订单已经存在超过30分钟没有支付,直接删除该订单
+        if (payOrderEntity != null && PayStatus.NON_PAY.getCode().equals(payOrderEntity.getStatus())) {
+            if (Math.abs(ChronoUnit.MINUTES.between(LocalDateTime.now(), payOrderEntity.getCreateTime())) > 30) {
+                // 再次查询订单状态
+                OrderStatusVO orderStatusVO = queryOrderStatus(payOrderEntity);
+                if (PayStatus.NON_PAY.getCode().equals(orderStatusVO.getStatus())) {
+                    this.removeById(payOrderEntity.getId());
+                    payOrderEntity = null;
+                }
+            }
+        }
+
+        CreateOrderResultVO createOrderResultVO = new CreateOrderResultVO();
+        if (payOrderEntity == null) {
+            createOrderResultVO.setMsg("新创建订单");
+
+            payOrderEntity = new PayOrderEntity();
+            payOrderEntity.setOrderNo(generateOrderNo());
+            payOrderEntity.setPayerIdentify(cardNumber);
+            payOrderEntity.setPayForIdentify(studentNo);
+            payOrderEntity.setStatus(PayStatus.NON_PAY.getCode());
+            payOrderEntity.setPayMethod(currentPayMethod);
+            payOrderEntity.setCreateTime(LocalDateTime.now());
+
+            // 支付金额、被支付用户信息
+            payOrderEntity.setOrderAmount(payableInfo.getRealPayAmount());
+            payOrderEntity.setStudentName(payableInfo.getStudentName());
+            payOrderEntity.setClassName(payableInfo.getClassName());
+
+            this.save(payOrderEntity);
+        } else {
+            if (!payOrderEntity.getPayMethod().equals(currentPayMethod)) {
+                payOrderEntity.setPayMethod(currentPayMethod);
+                this.updateById(payOrderEntity);
+            }
+            createOrderResultVO.setMsg("订单早已创建");
+        }
+
+        createOrderResultVO.setAmount(payOrderEntity.getOrderAmount());
+        createOrderResultVO.setOrderNo(payOrderEntity.getOrderNo());
+        createOrderResultVO.setPayMethod(payOrderEntity.getPayMethod());
+        return createOrderResultVO;
+    }
+
+    /**
+     * 根据订单号查询订单
+     * @param orderNo
+     * @return
+     */
+    @Override
+    public PayOrderEntity getByOrderNo(String orderNo) {
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("order_no", orderNo);
+        PayOrderEntity payOrderEntity = this.getOne(wrapper);
+        if (payOrderEntity == null) {
+            throw new RRException(BizCodeEnume.ORDER_IS_NOT_EXIT);
+        }
+
+        return payOrderEntity;
+    }
+
+    /**
+     * 查询订单状态
+     * @param cardNumber 支付者标识
+     * @param orderNo 订单号
+     * @return
+     */
+    @Override
+    public OrderStatusVO queryOrderStatus(String cardNumber, String orderNo) {
+        OrderStatusVO orderStatusVO = queryOrderStatus(orderNo);
+
+        if (!orderStatusVO.getPayerId().equals(cardNumber)) {
+            throw new RRException(BizCodeEnume.NO_PERMISSION);
+        }
+
+        return orderStatusVO;
+    }
+
+    @Override
+    public OrderStatusVO queryOrderStatus(PayOrderEntity payOrder) {
+        String orderNo = payOrder.getOrderNo();
+        // 订单未支付状态,先查询一下银行订单状态
+        if (PayStatus.NON_PAY.getCode().equalsIgnoreCase(payOrder.getStatus())) {
+            try {
+                if (PayMethod.CCB_PAY.getCode().equals(payOrder.getPayMethod())) {
+                    Map<String, String> map = payService.queryCCBOrderStatus(orderNo);
+                    String orderId = map.get("ORDERID");
+                    String statusCode = map.get("STATUSCODE");
+                    String amount = map.get("AMOUNT");
+                    if (orderNo.equals(orderId)
+                            && payOrder.getOrderAmount().setScale(2, BigDecimal.ROUND_UP).toString().equals(amount)
+                            && "1".equals(statusCode)) {
+                        // TODO 完成时间应该改成实际完成支付时间
+                        payOrder.setFinishTime(LocalDateTime.now());
+                        payOrder.setStatus(PayStatus.ALREADY_PAY.getCode());
+                        this.updateById(payOrder);
+                    }
+                } else if (PayMethod.JXNXS_PAY.getCode().equals(payOrder.getPayMethod())) {
+                    Map<String, String> map = payService.queryJXNXSOrderStatus(orderNo);
+                    String orderId = map.get("out_no");
+                    String statusCode = map.get("status");
+                    String amount = map.get("trade_amount");
+                    BigDecimal realPayAmount = new BigDecimal(amount).divide(new BigDecimal("100")).setScale(2, BigDecimal.ROUND_UP); // 分转成元
+
+                    if (orderNo.equals(orderId) && "1".equals(statusCode)) {
+                        if (Math.abs(payOrder.getOrderAmount().setScale(2, BigDecimal.ROUND_UP).subtract(realPayAmount).doubleValue()) < 0.01) {
+                            payOrder.setStatus(PayStatus.ALREADY_PAY.getCode());
+                            // TODO 完成时间应该改成实际完成支付时间
+                            payOrder.setFinishTime(LocalDateTime.now());
+                            this.updateById(payOrder);
+                        }
+                    }
+                }
+
+            } catch (RRException e) {
+                log.error("订单状态查询失败【{}】", e.getMessage());
+            }
+        }
+
+        OrderStatusVO orderStatusVO = new OrderStatusVO();
+        orderStatusVO.setOrderNo(orderNo);
+        orderStatusVO.setStatus(payOrder.getStatus());
+        orderStatusVO.setPayerId(payOrder.getPayerIdentify());
+        return orderStatusVO;
+    }
+
+    /**
+     * 查询订单状态
+     * @param orderNo 订单号
+     * @return
+     */
+    @Override
+    public OrderStatusVO queryOrderStatus(String orderNo) {
+        PayOrderEntity payOrder = getByOrderNo(orderNo);
+        return queryOrderStatus(payOrder);
+    }
+
+    /**
+     * 查询当前已完成支付金额
+     * @return
+     * @param queryTO
+     */
+    @Override
+    public BigDecimal queryCurrentPayedAmount(PayOrderQueryTO queryTO) {
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+        queryTO.setPayStatus(PayStatus.ALREADY_PAY.getCode());
+
+        commonWrapper(wrapper, queryTO);
+
+        wrapper.select("ifnull(sum(order_amount), 0) as total");
+        Map<String, Object> resultMap = this.getMap(wrapper);
+
+        return new BigDecimal(String.valueOf(resultMap.get("total"))).setScale(2, BigDecimal.ROUND_HALF_UP);
+    }
+
+    /**
+     * 下载完成缴费的名单
+     * @param response
+     * @param flag
+     * @param dateStr
+     */
+    @Override
+    public void downloadResult(HttpServletResponse response, String flag, String dateStr, String payMethod) throws IOException {
+
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("status", PayStatus.ALREADY_PAY.getCode());
+        wrapper.eq(StringUtils.hasText(payMethod), "pay_method", payMethod);
+        wrapper.apply("2".equals(flag) ,"date_format(finish_time, '%Y-%m') = {0}", dateStr);
+        wrapper.apply("3".equals(flag), "date_format(finish_time, '%Y-%m-%d') = {0}", dateStr);
+        wrapper.orderByDesc("finish_time").orderByAsc("pay_for_identify");
+
+        List<PayOrderEntity> parOrder = this.list(wrapper);
+        String fileName = URLEncoder.encode("已完成缴费学生名单" + new Date().getTime(), "UTF-8");
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+        EasyExcel.write(response.getOutputStream(), PayOrderEntity.class).sheet("已完成缴费学生名单").doWrite(parOrder);
+    }
+
+    @Override
+    public Integer queryCurrentPayedRecordNum(PayOrderQueryTO queryTO) {
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+        queryTO.setPayStatus(PayStatus.ALREADY_PAY.getCode());
+
+        commonWrapper(wrapper, queryTO);
+
+        return this.count(wrapper);
+    }
+
+    /**
+     * 生成订单号
+     * @return
+     */
+    private String generateOrderNo() {
+        String idStr = IdWorker.getIdStr();
+        QueryWrapper<PayOrderEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("order_no", idStr);
+        PayOrderEntity one = this.getOne(wrapper);
+        if (one == null) {
+            return idStr;
+        } else {
+            return generateOrderNo();
+        }
+    }
+
+    /**
+     * 构建公共的查询wrapper
+     * @param wrapper
+     * @param queryTO
+     */
+    private void commonWrapper(QueryWrapper<PayOrderEntity> wrapper, PayOrderQueryTO queryTO) {
+        wrapper.eq(StringUtils.hasText(queryTO.getPayStatus()), "status", queryTO.getPayStatus());
+        wrapper.eq(StringUtils.hasText(queryTO.getPayMethod()), "pay_method", queryTO.getPayMethod());
+        String keyword = queryTO.getKeyword();
+        if (StringUtils.hasText(keyword)) {
+            wrapper.and(w -> w.like("student_name", keyword)
+                    .or()
+                    .like("class_name", keyword)
+                    .or()
+                    .eq("order_no", keyword));
+        }
+
+        String flag = queryTO.getFlag();
+        String dateStr = queryTO.getDateStr();
+        wrapper.apply("2".equals(flag) ,"date_format(finish_time, '%Y-%m') = {0}", dateStr);
+        wrapper.apply("3".equals(flag), "date_format(finish_time, '%Y-%m-%d') = {0}", dateStr);
+    }
+}

Plik diff jest za duży
+ 453 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/PayServiceImpl.java


+ 144 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/service/impl/PayableInfoServiceImpl.java

@@ -0,0 +1,144 @@
+package com.chuanghai.smartschool.tuitionpayment.service.impl;
+
+import com.alibaba.excel.EasyExcel;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume;
+import com.chuanghai.smartschool.tuitionpayment.common.exception.RRException;
+import com.chuanghai.smartschool.tuitionpayment.listener.aliexcel.MyPayableInfoImportListener;
+import com.chuanghai.smartschool.tuitionpayment.service.EnablePayLimitByGradeService;
+import com.chuanghai.smartschool.tuitionpayment.service.PayOrderService;
+import com.chuanghai.smartschool.tuitionpayment.utils.MyUtil;
+import com.chuanghai.smartschool.tuitionpayment.vo.StudentPayableInfoVO;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.MyQuery;
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageParam;
+
+import com.chuanghai.smartschool.tuitionpayment.dao.PayableInfoDao;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayableInfoEntity;
+import com.chuanghai.smartschool.tuitionpayment.service.PayableInfoService;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.time.LocalDate;
+
+
+@Service("payableInfoService")
+public class PayableInfoServiceImpl extends ServiceImpl<PayableInfoDao, PayableInfoEntity> implements PayableInfoService {
+
+    @Autowired
+    private PayOrderService payOrderService;
+    @Autowired
+    private EnablePayLimitByGradeService enablePayLimitByGradeService;
+
+    @Override
+    public PageUtils queryPage(String keyword, PageParam pageParam) {
+        QueryWrapper<PayableInfoEntity> wrapper = new QueryWrapper<>();
+
+        if (StringUtils.hasText(keyword)) {
+            wrapper.and(w -> w.like("student_no", keyword)
+                                .or()
+                                .like("student_name", keyword));
+        }
+
+        IPage<PayableInfoEntity> page = this.page(
+                new MyQuery<PayableInfoEntity>().getPage(pageParam),
+                wrapper
+        );
+
+        return new PageUtils(page);
+    }
+
+    /**
+     * 根据学号查询用户缴费信息
+     * @param cardNumber
+     * @return
+     */
+    @Override
+    public StudentPayableInfoVO getPayableInfoByCardNumber(String cardNumber) {
+        // 查询用户基本的缴费信息
+        QueryWrapper<PayableInfoEntity> wrapper = new QueryWrapper();
+        wrapper.eq("student_no", cardNumber);
+        return getPayableInfo(wrapper, cardNumber);
+    }
+
+    /**
+     * 根据学号和学生姓名获取缴费信息
+     * @param studentNo 学号
+     * @param studentName 学生姓名
+     * @return
+     */
+    @Override
+    public StudentPayableInfoVO getPayableInfoByStudentNoAndName(String studentNo, String studentName) {
+        // 查询用户基本的缴费信息
+        QueryWrapper<PayableInfoEntity> wrapper = new QueryWrapper();
+        wrapper.eq("student_no", studentNo);
+        wrapper.eq("student_name", studentName);
+
+        return getPayableInfo(wrapper, studentNo);
+    }
+
+    /**
+     * 根据学号查询缴费信息
+     * @param studentNo
+     * @return
+     */
+    @Override
+    public PayableInfoEntity getByStudentNo(String studentNo) {
+        QueryWrapper<PayableInfoEntity> wrapper = new QueryWrapper();
+        wrapper.eq("student_no", studentNo);
+        PayableInfoEntity payableInfoEntity = this.getOne(wrapper);
+        if (payableInfoEntity == null) {
+            throw new RRException(BizCodeEnume.PAYABLE_INFO_IS_NOT_EXIT);
+        }
+
+        return payableInfoEntity;
+    }
+
+    /**
+     * 导入学生应缴信息原始数据
+     * @param file
+     */
+    @Transactional
+    @Override
+    public void importByExcel(MultipartFile file) throws IOException {
+        // 清空表数据
+        this.truncateTable();
+
+        EasyExcel.read(file.getInputStream(),
+                PayableInfoEntity.class,
+                new MyPayableInfoImportListener(this))
+                .sheet()
+                .doRead();
+    }
+
+    /**
+     * 清空表数据
+     */
+    @Override
+    public void truncateTable() {
+        this.baseMapper.truncateTable();
+    }
+
+    private StudentPayableInfoVO getPayableInfo(QueryWrapper<PayableInfoEntity> wrapper, String studentNo) {
+        PayableInfoEntity payableInfoEntity = this.getOne(wrapper);
+        if (payableInfoEntity == null) {
+            throw new RRException(BizCodeEnume.PAYABLE_INFO_IS_NOT_EXIT);
+        }
+
+        StudentPayableInfoVO studentPayableInfo = new StudentPayableInfoVO();
+        BeanUtils.copyProperties(payableInfoEntity, studentPayableInfo);
+
+        // 查询用户是否已经缴费
+        boolean alreadyPay = payOrderService.isAlreadyPay(studentNo);
+        studentPayableInfo.setPay(alreadyPay);
+
+        return studentPayableInfo;
+    }
+}

+ 57 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/to/DeletePayableInfoTO.java

@@ -0,0 +1,57 @@
+package com.chuanghai.smartschool.tuitionpayment.to;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @Author: codingliang
+ * @Description: 删除缴费信息TO
+ * @Date: 2021-08-20 11:22
+ * @Version: V1.0
+ **/
+@Data
+public class DeletePayableInfoTO {
+
+    /**
+     * 学号
+     */
+    private String studentNo;
+    /**
+     * 姓名
+     */
+    private String studentName;
+    /**
+     * 年级
+     */
+    private String grade;
+    /**
+     * 收费年度
+     */
+    private String years;
+    /**
+     * 学院名称
+     */
+    private String collegeName;
+    /**
+     * 专业名称
+     */
+    private String majorName;
+    /**
+     * 班级名称
+     */
+    private String className;
+    /**
+     * 应付金额
+     */
+    private BigDecimal realPayAmount;
+    /**
+     * 应付详情 格式:教材费:1000\学费:18000\住宿费:1800
+     */
+    private String payItemDetail;
+
+    /**
+     * 提示信息
+     */
+    private String msg;
+}

+ 33 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/to/FeedbackMsgCreateTO.java

@@ -0,0 +1,33 @@
+package com.chuanghai.smartschool.tuitionpayment.to;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+/**
+ * @Author: codingliang
+ * @Description: 反馈信息创建TO
+ * @Date: 2021-08-13 9:25
+ * @Version: V1.0
+ **/
+@Data
+public class FeedbackMsgCreateTO {
+
+    /**
+     * 反馈者姓名
+     */
+    @NotBlank(message = "反馈者姓名不能为空")
+    private String feedbackPersonName;
+    /**
+     * 反馈者电话
+     */
+    @NotBlank(message = "反馈者电话不能为空")
+    private String feedbackPersonPhone;
+    /**
+     * 反馈问题,不要超过512个字符
+     */
+    @NotBlank(message = "反馈信息不能为空")
+    @Size(max = 512, message = "反馈信息内容超长")
+    private String feedbackInfo;
+}

+ 39 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/to/PayOrderQueryTO.java

@@ -0,0 +1,39 @@
+package com.chuanghai.smartschool.tuitionpayment.to;
+
+import lombok.Data;
+
+/**
+ * @Author: codingliang
+ * @Description: 支付订单查询to
+ * @Date: 2021-08-13 11:10
+ * @Version: V1.0
+ **/
+@Data
+public class PayOrderQueryTO {
+
+    /**
+     * 支付状态 不传查所有,1未支付、2支付成功
+     */
+    private String payStatus;
+
+    /**
+     * 支付方式 不传查所有,1建行支付、2江西农商行支付
+     * @since 0.1.0
+     */
+    private String payMethod;
+
+    /**
+     * 关键字,支持缴费用户姓名、班级、订单号搜索
+     */
+    private String keyword;
+
+    /**
+     * 查询标志,1查询所有数据、2查询指定月份数据、3查询指定日期数据
+     */
+    private String flag;
+
+    /**
+     * 月份或日期,当flag为2、3时必传,月份格式:yyyy-MM、日期格式:yyyy-MM-dd
+     */
+    private String dateStr;
+}

+ 95 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/CCBRSASig.java

@@ -0,0 +1,95 @@
+package com.chuanghai.smartschool.tuitionpayment.utils;
+
+import netpay.merchant.crypto.ABAProvider;
+import netpay.merchant.crypto.RSAPrivKeyCrt;
+import netpay.merchant.crypto.RSAPubKey;
+
+import java.security.Security;
+import java.security.Signature;
+
+/**
+ * @Author: codingliang
+ * @Description: 建行Rsa工具
+ * @Date: 2021-08-03 16:11
+ * @Version: V1.0
+ **/
+public class CCBRSASig {
+
+    /**
+     * 生成签名
+     * @param src 原始串
+     * @param priKey 私钥
+     * @return
+     */
+    public static String generateSigature(String src, String priKey) {
+        try {
+            Security.addProvider(new ABAProvider());
+            Signature sigEng = Signature.getInstance("MD5withRSA", "ABA");
+
+            byte[] pribyte = hexStrToBytes(priKey.trim());
+            sigEng.initSign(new RSAPrivKeyCrt(pribyte));
+            sigEng.update(src.getBytes());
+
+            byte[] signature = sigEng.sign();
+            return bytesToHexStr(signature);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * 验证签名
+     * @param sign 签名
+     * @param src 原始串
+     * @param pubKey 公钥
+     * @return
+     */
+    public static boolean verifySigature(String sign, String src, String pubKey) {
+        try {
+            Security.addProvider(new ABAProvider());
+            Signature sigEng = Signature.getInstance("MD5withRSA", "ABA");
+
+            byte[] pubbyte = hexStrToBytes(pubKey.trim());
+            sigEng.initVerify(new RSAPubKey(pubbyte));
+            sigEng.update(src.getBytes());
+
+            byte[] sign1 = hexStrToBytes(sign);
+            if (sigEng.verify(sign1)) {
+                return true;
+            } else {
+                return false;
+            }
+
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    private static final String bytesToHexStr(
+            byte[] bcd) {
+        StringBuffer s = new StringBuffer(bcd.length * 2);
+
+        for (int i = 0; i < bcd.length; i++) {
+            s.append(bcdLookup[(bcd[i] >>> 4) & 0x0f]);
+            s.append(bcdLookup[bcd[i] & 0x0f]);
+        }
+
+        return s.toString();
+    }
+
+    private static final byte[] hexStrToBytes(
+            String s) {
+        byte[] bytes;
+
+        bytes = new byte[s.length() / 2];
+
+        for (int i = 0; i < bytes.length; i++) {
+            bytes[i] = (byte) Integer.parseInt(
+                    s.substring(2 * i, 2 * i + 2), 16);
+        }
+
+        return bytes;
+    }
+
+    private static final char[] bcdLookup = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+}

+ 164 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/JXNXSPayUtil.java

@@ -0,0 +1,164 @@
+package com.chuanghai.smartschool.tuitionpayment.utils;
+
+
+import org.springframework.boot.configurationprocessor.json.JSONObject;
+import org.springframework.util.StringUtils;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+
+/**
+ * 农商行支付工具类
+ *
+ * Description
+ * Create 2017-03-07 14:01:23
+ *
+ * @author Benny.YEE
+ */
+public class JXNXSPayUtil {
+
+    public static String SHA1(String decript) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA-1");
+            digest.update(decript.getBytes("UTF-8"));
+            byte[] messageDigest = digest.digest();
+            StringBuilder hexString = new StringBuilder();
+            for (byte message : messageDigest) {
+                String shaHex = Integer.toHexString(message & 0xFF);
+                if (shaHex.length() < 2)
+                    hexString.append(0);
+
+                hexString.append(shaHex);
+            }
+            return hexString.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    /**
+     * 签名
+     *
+     * @param data
+     * @return
+     */
+    public static String sign(Map<String, String> data) {
+        String sign = null;
+
+        try {
+            // A~z排序 (加上open_key)
+            Set<String> keySet = data.keySet();
+            String[] keyArray = keySet.toArray(new String[keySet.size()]);
+            Arrays.sort(keyArray);
+            StringBuilder sb = new StringBuilder();
+            for (String k : keyArray) {
+                if (k.equals("sign")) {  // 删除sign节点
+                    continue;
+                }
+                sb.append(k).append("=").append(data.get(k).trim()).append("&");
+            }
+
+            String sbStr = sb.toString();
+            String sortStr = sbStr.substring(0, sbStr.length() - 1);
+
+            // sha1加密(小写)
+            String sha1 = SHA1(sortStr).toLowerCase();
+
+            // md5加密(小写)
+            MessageDigest md5 = MessageDigest.getInstance("MD5");
+            byte[] digest = md5.digest(sha1.getBytes("utf-8"));
+            String resultString = byte2hex(digest);
+
+            sign = resultString.toLowerCase();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return sign;
+    }
+
+    /**
+     * AES加密,再二进制转十六进制(bin2hex)
+     * @param dataJsonStr 需要加密的字符串
+     * @param key key
+     * @throws Exception
+     */
+    public static String handleEncrypt(String dataJsonStr, String key) throws Exception {
+
+        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("ASCII"), "AES");
+        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+        cipher.init(1, skeySpec);
+        byte[] encrypted = cipher.doFinal(dataJsonStr.getBytes("UTF-8"));
+
+        return byte2hex(encrypted);
+    }
+
+    public static String decrypt(String sSrc, String sKey) throws Exception {
+        SecretKeySpec skeySpec = new SecretKeySpec(sKey.getBytes("ASCII"), "AES");
+        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+        cipher.init(2, skeySpec);
+        byte[] encrypted1 = hex2byte(sSrc);
+        byte[] original = cipher.doFinal(encrypted1);
+        return new String(original, "UTF-8");
+    }
+
+    /**
+     * 验签
+     *
+     * @param params
+     * @param key 农商openKey
+     * @return
+     */
+    public static Boolean verifySign(Map<String, String> params, String key) {
+        String respSign = params.get("sign");
+
+        if (StringUtils.hasText(respSign)) {
+            params.put("open_key", key);
+            String veriSign = sign(params);
+            if (respSign.equals(veriSign)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * byte数组转十六进制字符串
+     */
+    public static String byte2hex(byte[] result) {
+        StringBuffer sb = new StringBuffer(result.length * 2);
+        for (int i = 0; i < result.length; i++) {
+            int hight = ((result[i] >> 4) & 0x0f);
+            int low = result[i] & 0x0f;
+            sb.append(hight > 9 ? (char) ((hight - 10) + 'a') : (char) (hight + '0'));
+            sb.append(low > 9 ? (char) ((low - 10) + 'a') : (char) (low + '0'));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 十六进制字符串转byte数组
+     */
+    public static byte[] hex2byte(String strhex) {
+        if (strhex == null)
+            return null;
+
+        int l = strhex.length();
+        if (l % 2 == 1)
+            return null;
+
+        byte[] b = new byte[l / 2];
+        for (int i = 0; i != l / 2; ++i)
+            b[i] = (byte) Integer.parseInt(strhex.substring(i * 2, i * 2 + 2), 16);
+
+        return b;
+    }
+
+}

+ 110 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/MyAES.java

@@ -0,0 +1,110 @@
+package com.chuanghai.smartschool.tuitionpayment.utils;
+
+import com.chuanghai.smartschool.tuitionpayment.config.WeixiaoConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * @Author: codingliang
+ * @Description: 微校接口数据加解密
+ * @Date: 2021-06-21 10:26
+ * @Version: V1.0
+ **/
+@Component
+public class MyAES {
+
+    @Autowired
+    private WeixiaoConfig weixiaoConfig;
+
+    /**
+     * 加密
+     * @param sSrc 需要加密的字串
+     * @return
+     * @throws Exception
+     */
+    public String encrypt(String sSrc) throws Exception {
+        String sKey = weixiaoConfig.getAppKey();
+        String sIv = weixiaoConfig.getAppSecret();
+        sIv = sIv.substring(0, 16); // app_secret 取前16位
+
+        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+        int blockSize = cipher.getBlockSize();
+
+        byte[] dataBytes = sSrc.getBytes();
+        int plaintextLength = dataBytes.length;
+        if (plaintextLength % blockSize != 0) {
+            plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
+        }
+
+        byte[] plaintext = new byte[plaintextLength];
+        System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
+
+        SecretKeySpec keyspec = new SecretKeySpec(sKey.getBytes(), "AES");
+        IvParameterSpec ivspec = new IvParameterSpec(sIv.getBytes());
+
+        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
+        byte[] encrypted = cipher.doFinal(plaintext);
+
+        return byte2hex(encrypted).toLowerCase();
+    }
+
+    /**
+     * 解密
+     * @param sSrc 需要解密的字串
+     * @return
+     * @throws Exception
+     */
+    public String decrypt(String sSrc) throws Exception {
+        String sKey = weixiaoConfig.getAppKey();
+        String sIv = weixiaoConfig.getAppSecret();
+        sIv = sIv.substring(0, 16); // app_secret 取前16位
+
+        byte[] encrypted1 = hex2byte(sSrc);
+
+        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+        SecretKeySpec keyspec = new SecretKeySpec(sKey.getBytes(), "AES");
+        IvParameterSpec ivspec = new IvParameterSpec(sIv.getBytes());
+
+        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
+
+        byte[] original = cipher.doFinal(encrypted1);
+        String originalString = new String(original);
+
+        return originalString;
+    }
+
+    private byte[] hex2byte(String strhex) {
+        if (strhex == null) {
+            return null;
+        }
+        int l = strhex.length();
+        if (l % 2 == 1) {
+            return null;
+        }
+        byte[] b = new byte[l / 2];
+        for (int i = 0; i != l / 2; i++) {
+            b[i] = (byte) Integer.parseInt(strhex.substring(i * 2, i * 2 + 2),
+                    16);
+        }
+
+        return b;
+    }
+
+    private String byte2hex(byte[] b) {
+        String hs = "";
+        String stmp = "";
+        for (int n = 0; n < b.length; n++) {
+            stmp = (Integer.toHexString(b[n] & 0XFF));
+            if (stmp.length() == 1) {
+                hs = hs + "0" + stmp;
+            } else {
+                hs = hs + stmp;
+            }
+        }
+        return hs.toUpperCase();
+    }
+}

+ 96 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/MySignatureUtil.java

@@ -0,0 +1,96 @@
+package com.chuanghai.smartschool.tuitionpayment.utils;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @Author: codingliang
+ * @Description: 签名工具
+ * @Date: 2021-07-02 10:53
+ * @Version: V1.0
+ **/
+public class MySignatureUtil {
+
+    public enum SignType {
+        MD5, HMACSHA256
+    }
+
+    /**
+     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
+     *
+     * 设所有发送或者接收到的数据为集合M,将集合M内参数值的参数按照参数名ASCII码从小到大排序(字典序),
+     * 使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 特别注意以下重要规则:
+     *      参数名ASCII码从小到大排序(字典序);
+     *      如果参数的值为空不参与签名;
+     *      参数名区分大小写;
+     *      验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
+     * 在stringA最后拼接上&key=APP_SECRET得到stringSignTemp字符串,
+     * 并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
+     * @param data 待签名数据
+     * @param key API密钥
+     * @param signType 签名方式
+     * @return 签名
+     */
+    public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
+        Set<String> keySet = data.keySet();
+        String[] keyArray = keySet.toArray(new String[keySet.size()]);
+        Arrays.sort(keyArray);
+        StringBuilder sb = new StringBuilder();
+        for (String k : keyArray) {
+            if (k.equals("sign")) {
+                continue;
+            }
+            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
+                sb.append(k).append("=").append(data.get(k).trim()).append("&");
+        }
+        sb.append("key=").append(key);
+        if (SignType.MD5.equals(signType)) {
+            return MD5(sb.toString()).toUpperCase();
+        }
+        else if (SignType.HMACSHA256.equals(signType)) {
+            return HMACSHA256(sb.toString(), key);
+        }
+        else {
+            throw new Exception(String.format("Invalid sign_type: %s", signType));
+        }
+    }
+
+    /**
+     * 生成 MD5
+     *
+     * @param data 待处理数据
+     * @return MD5结果
+     */
+    public static String MD5(String data) throws Exception {
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        byte[] array = md.digest(data.getBytes("UTF-8"));
+        StringBuilder sb = new StringBuilder();
+        for (byte item : array) {
+            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
+        }
+        return sb.toString().toUpperCase();
+    }
+
+    /**
+     * 生成 HMACSHA256
+     * @param data 待处理数据
+     * @param key 密钥
+     * @return 加密结果
+     * @throws Exception
+     */
+    public static String HMACSHA256(String data, String key) throws Exception {
+        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
+        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
+        sha256_HMAC.init(secret_key);
+        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
+        StringBuilder sb = new StringBuilder();
+        for (byte item : array) {
+            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
+        }
+        return sb.toString().toUpperCase();
+    }
+}

+ 38 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/utils/MyUtil.java

@@ -0,0 +1,38 @@
+package com.chuanghai.smartschool.tuitionpayment.utils;
+
+import java.time.LocalDate;
+
+/**
+ * @Author: codingliang
+ * @Description: 自定义工具类
+ * @Date: 2021-08-20 10:36
+ * @Version: V1.0
+ **/
+public class MyUtil {
+
+    /**
+     * 计算学生年级
+     * @param grade 入学年份
+     * @return 年级,如当前是2021年,学生入学年份是2021,则返回 1
+     */
+    public static Integer calcStudentGrade(Integer grade) {
+        LocalDate now = LocalDate.now();
+        int currentYear = now.getYear();
+        int currentMonth = now.getMonthValue();
+
+        int currentGrade = currentYear - grade;
+        if (currentGrade < 0) {
+            return 1;
+        } else {
+            if (currentMonth < 7) {
+                return currentYear - grade;
+            } else {
+                return currentYear - grade + 1;
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        System.out.println(calcStudentGrade(2022));
+    }
+}

+ 34 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/CreateOrderResultVO.java

@@ -0,0 +1,34 @@
+package com.chuanghai.smartschool.tuitionpayment.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @Author: codingliang
+ * @Description: 创建订单成功VO
+ * @Date: 2021-08-12 17:18
+ * @Version: V1.0
+ **/
+@Data
+public class CreateOrderResultVO {
+
+    /**
+     * 订单号
+     */
+    private String orderNo;
+    /**
+     * 信息
+     */
+    private String msg;
+    /**
+     * 支付方式 1建行支付、2农商行支付
+     * @since 0.1.0
+     */
+    private String payMethod;
+    /**
+     * 支付金额
+     * @since 0.1.0
+     */
+    private BigDecimal amount;
+}

+ 20 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/CurrentPayMethodVO.java

@@ -0,0 +1,20 @@
+package com.chuanghai.smartschool.tuitionpayment.vo;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @Author: codingliang
+ * @Description: 当前支付方式VO
+ * @Date: 2021-08-19 11:42
+ * @Version: V1.0
+ **/
+@Data
+@Builder
+public class CurrentPayMethodVO {
+
+    /**
+     * 当前支付方式 1建行支付、2江西农商行支付
+     */
+    private String currentPayMethod;
+}

+ 26 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/OrderStatusVO.java

@@ -0,0 +1,26 @@
+package com.chuanghai.smartschool.tuitionpayment.vo;
+
+import lombok.Data;
+
+/**
+ * @Author: codingliang
+ * @Description: 订单状态VO
+ * @Date: 2021-08-12 18:57
+ * @Version: V1.0
+ **/
+@Data
+public class OrderStatusVO {
+
+    /**
+     * 订单号
+     */
+    private String orderNo;
+    /**
+     * 订单状态 1未支付、2支付成功
+     */
+    private String status;
+    /**
+     * 支付者id
+     */
+    private String payerId;
+}

+ 32 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/PayOrderListVO.java

@@ -0,0 +1,32 @@
+package com.chuanghai.smartschool.tuitionpayment.vo;
+
+import com.chuanghai.smartschool.tuitionpayment.common.utils.PageUtils;
+import com.chuanghai.smartschool.tuitionpayment.entity.PayOrderEntity;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @Author: codingliang
+ * @Description: 支付订单列表VO
+ * @Date: 2021-08-13 11:29
+ * @Version: V1.0
+ **/
+@Data
+public class PayOrderListVO {
+
+    /**
+     * 当前已支付总金额
+     */
+    private BigDecimal total;
+
+    /**
+     * 当前已支付的数量
+     */
+    private Integer recordsNum;
+
+    /**
+     * 分页数据
+     */
+    private PageUtils<PayOrderEntity> page;
+}

+ 21 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/StudentNoVO.java

@@ -0,0 +1,21 @@
+package com.chuanghai.smartschool.tuitionpayment.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+/**
+ * @Author: codingliang
+ * @Description: 学号vo
+ * @Date: 2021-08-20 11:42
+ * @Version: V1.0
+ **/
+@Data
+public class StudentNoVO {
+
+    /**
+     * 学号
+     */
+    @ExcelProperty("学号")
+    private String studentNo;
+
+}

+ 21 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/StudentPayableInfoVO.java

@@ -0,0 +1,21 @@
+package com.chuanghai.smartschool.tuitionpayment.vo;
+
+import com.chuanghai.smartschool.tuitionpayment.entity.PayableInfoEntity;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * @Author: codingliang
+ * @Description: 学生应付金额详情
+ * @Date: 2021-08-12 16:43
+ * @Version: V1.0
+ **/
+@Data
+public class StudentPayableInfoVO extends PayableInfoEntity {
+
+    /**
+     * 是否已缴费
+     */
+    @JsonProperty("pay")
+    private boolean isPay;
+}

+ 28 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/UserTokenVO.java

@@ -0,0 +1,28 @@
+package com.chuanghai.smartschool.tuitionpayment.vo;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @Author: codingliang
+ * @Description: 用户token
+ * @Date: 2021-04-29 15:45
+ * @Version: V1.0
+ **/
+@Data
+@Builder
+public class UserTokenVO {
+
+    /**
+     * 用户名
+     */
+    private String userName;
+    /**
+     * token
+     */
+    private String token;
+    /**
+     * token过期时间
+     */
+    private long expireTime;
+}

+ 44 - 0
src/main/java/com/chuanghai/smartschool/tuitionpayment/vo/WechatJsApiPayParamVO.java

@@ -0,0 +1,44 @@
+package com.chuanghai.smartschool.tuitionpayment.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import lombok.Builder;
+import lombok.Data;
+import lombok.ToString;
+
+/**
+ * @Author: codingliang
+ * @Description: 微信Jsapi支付VO
+ * @Date: 2021-08-12 18:16
+ * @Version: V1.0
+ **/
+@Builder
+@Data
+public class WechatJsApiPayParamVO {
+
+    /**
+     * 发起支付的appid
+     */
+    private String appId;
+    /**
+     * 时间戳
+     */
+    private String timeStamp;
+    /**
+     * 随机串
+     */
+    private String nonceStr;
+    /**
+     * package信息
+     */
+    @JsonProperty(value = "package")
+    private String packageStr;
+    /**
+     * 签名方式
+     */
+    private String signType;
+    /**
+     * 签名
+     */
+    private String paySign;
+}

+ 58 - 0
src/main/resources/application.yml

@@ -0,0 +1,58 @@
+server:
+  port: 9999
+
+# 数据源
+spring:
+  datasource:
+    username: root
+    password: Chuanghai_2021.
+    url: jdbc:mysql://192.168.1.38:3306/tuition_payment
+    driver-class-name: com.mysql.cj.jdbc.Driver
+
+# mybatis plus
+mybatis-plus:
+  mapper-locations: classpath:/mapper/**/*.xml
+  global-config:
+    db-config:
+      id-type: auto
+
+# 前端应用地址
+fontendUrl: http://192.168.31.111:8080/jiaofei/jiaofeiH5
+
+# 微校相关配置
+weixiao:
+  appKey: 2DDC9DBF32F28845
+  appSecret: 97F7E3A65AD8A1C500DDD90F3D57EBE0
+  fontendUrl: ${fontendUrl}
+
+# 微信相关配置
+wechat:
+  appid: wxd87cbe1db0437303
+  secret: 18e17f97e674e1c03fc255f12d12ca4d
+  fontendUrl: ${fontendUrl}
+
+# 建行相关配置
+ccb:
+  # 商户代码
+  merchantid: 105000082202756
+  # 柜台代码
+  posid: "056859026"
+  # 分行代码
+  branchid: 360000000
+  # 操作密码
+  qupwd: ncjtxy2020
+  # 公钥-后30位
+  pubShort: 5a557e34fcdc65f6a7af2785020111
+  # 公钥-完整
+  pub: 30819d300d06092a864886f70d010101050003818b0030818702818100de5637c55fea3904815c09c7af61acab34c0f57d7b415ebed4afd23445d05f63dc3923ba37948b92bc11a2fed074e248785b32d9cd2de90ce3012bf6f601f2a4bd34a0c5b3058f6321e43d4eea9bbb0a30dfd8ccccc702e238072289b6dc28c21e0e5d3f392d67e11da34fc59cabdbe293be4e125a557e34fcdc65f6a7af2785020111
+  # 公众号id
+  wxAppId: wxd87cbe1db0437303
+
+# 江西农商行相关配置
+jxnxs:
+  openId: 5494ec3310685daa218382619dd20e27
+  openKey: 611764d05a545bb7bb08cbd457f7dac1
+
+logging:
+  level:
+    com.chuanghai: debug

+ 16 - 0
src/main/resources/mapper/tuitionpayment/AdminInfoDao.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.chuanghai.smartschool.tuitionpayment.dao.AdminInfoDao">
+
+	<!-- 可根据自己的需求,是否要使用 -->
+    <resultMap type="com.chuanghai.smartschool.tuitionpayment.entity.AdminInfoEntity" id="adminInfoMap">
+        <result property="id" column="id"/>
+        <result property="userName" column="user_name"/>
+        <result property="password" column="password"/>
+        <result property="adminType" column="admin_type"/>
+        <result property="status" column="status"/>
+    </resultMap>
+
+
+</mapper>

+ 14 - 0
src/main/resources/mapper/tuitionpayment/EnablePayLimitByGradeDao.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.chuanghai.smartschool.tuitionpayment.dao.EnablePayLimitByGradeDao">
+
+    <!-- 可根据自己的需求,是否要使用 -->
+    <resultMap type="com.chuanghai.smartschool.tuitionpayment.entity.EnablePayLimitByGradeEntity" id="adminInfoMap">
+        <result property="id" column="id"/>
+        <result property="gradeName" column="grade_name"/>
+        <result property="grade" column="grade"/>
+        <result property="enablePay" column="enable_pay"/>
+    </resultMap>
+
+</mapper>

+ 17 - 0
src/main/resources/mapper/tuitionpayment/FeedbackMsgDao.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.chuanghai.smartschool.tuitionpayment.dao.FeedbackMsgDao">
+
+	<!-- 可根据自己的需求,是否要使用 -->
+    <resultMap type="com.chuanghai.smartschool.tuitionpayment.entity.FeedbackMsgEntity" id="feedbackMsgMap">
+        <result property="id" column="id"/>
+        <result property="feedbackPersonIdentify" column="feedback_person_identify"/>
+        <result property="feedbackPersonName" column="feedback_person_name"/>
+        <result property="feedbackPersonPhone" column="feedback_person_phone"/>
+        <result property="feedbackInfo" column="feedback_info"/>
+        <result property="statu" column="statu"/>
+    </resultMap>
+
+
+</mapper>

+ 13 - 0
src/main/resources/mapper/tuitionpayment/PayMethodSettingDao.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.chuanghai.smartschool.tuitionpayment.dao.PayMethodSettingDao">
+
+	<!-- 可根据自己的需求,是否要使用 -->
+    <resultMap type="com.chuanghai.smartschool.tuitionpayment.entity.PayMethodSettingEntity" id="payableInfoMap">
+        <result property="id" column="id"/>
+        <result property="payMethodName" column="pay_method_name"/>
+        <result property="payMethod" column="pay_method"/>
+        <result property="isCurrentPay" column="is_current_pay"/>
+    </resultMap>
+</mapper>

+ 21 - 0
src/main/resources/mapper/tuitionpayment/PayOrderDao.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.chuanghai.smartschool.tuitionpayment.dao.PayOrderDao">
+
+	<!-- 可根据自己的需求,是否要使用 -->
+    <resultMap type="com.chuanghai.smartschool.tuitionpayment.entity.PayOrderEntity" id="payOrderMap">
+        <result property="id" column="id"/>
+        <result property="orderNo" column="order_no"/>
+        <result property="payerIdentify" column="payer_identify"/>
+        <result property="payForIdentify" column="pay_for_identify"/>
+        <result property="studentName" column="student_name"/>
+        <result property="className" column="class_name"/>
+        <result property="orderAmount" column="order_amount"/>
+        <result property="status" column="status"/>
+        <result property="createTime" column="create_time"/>
+        <result property="finishTime" column="finish_time"/>
+    </resultMap>
+
+
+</mapper>

+ 23 - 0
src/main/resources/mapper/tuitionpayment/PayableInfoDao.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.chuanghai.smartschool.tuitionpayment.dao.PayableInfoDao">
+
+	<!-- 可根据自己的需求,是否要使用 -->
+    <resultMap type="com.chuanghai.smartschool.tuitionpayment.entity.PayableInfoEntity" id="payableInfoMap">
+        <result property="id" column="id"/>
+        <result property="studentNo" column="student_no"/>
+        <result property="studentName" column="student_name"/>
+        <result property="grade" column="grade"/>
+        <result property="years" column="years"/>
+        <result property="collegeName" column="college_name"/>
+        <result property="majorName" column="major_name"/>
+        <result property="className" column="class_name"/>
+        <result property="realPayAmount" column="real_pay_amount"/>
+        <result property="payItemDetail" column="pay_item_detail"/>
+    </resultMap>
+
+    <update id="truncateTable" >
+        delete from payable_info
+    </update>
+</mapper>

+ 83 - 0
src/main/resources/smart-doc.json

@@ -0,0 +1,83 @@
+{
+  "serverUrl": "http://192.168.161.230:9999",
+  "isStrict": false,
+  "allInOne": true,
+  "outPath": "./src/main/resources/static/doc",
+  "coverOld": true,
+  "style": "zenburn",
+  "createDebugPage": false,
+  "projectName": "学费缴纳系统",
+  "inlineEnum": true,
+  "revisionLogs": [
+    {
+      "version": "0.0.1",
+      "revisionTime":"2021-08-12 18:50:00",
+      "status": "创建文档",
+      "author": "codingliang",
+      "remarks": "初始化接口文档"
+    },
+    {
+      "version": "0.0.2",
+      "revisionTime":"2021-08-13 10:15:00",
+      "status": "文档更新",
+      "author": "codingliang",
+      "remarks": "新增客户端接口文档"
+    },
+    {
+      "version": "0.0.3",
+      "revisionTime":"2021-08-13 12:43:00",
+      "status": "文档更新",
+      "author": "codingliang",
+      "remarks": "新增管理端接口文档"
+    },
+    {
+      "version": "0.0.4",
+      "revisionTime":"2021-08-14 16:43:00",
+      "status": "文档更新",
+      "author": "codingliang",
+      "remarks": "新增建行支付成功同步跳转地址"
+    },
+    {
+      "version": "0.1.0",
+      "revisionTime":"2021-08-19 11:33:00",
+      "status": "文档更新",
+      "author": "codingliang",
+      "remarks": "新增江西农商行支付"
+    },
+    {
+      "version": "0.1.1",
+      "revisionTime":"2021-08-20 11:24:51",
+      "status": "文档更新",
+      "author": "codingliang",
+      "remarks": "新增年级支付限制"
+    }
+  ],
+  "dataDictionaries": [
+    {
+      "title": "支付状态枚举",
+      "enumClassName": "com.chuanghai.smartschool.tuitionpayment.enume.PayStatus",
+      "codeField": "code",
+      "descField": "msg"
+    },
+    {
+      "title": "反馈信息状态枚举",
+      "enumClassName": "com.chuanghai.smartschool.tuitionpayment.enume.FeedbackMsgStatus",
+      "codeField": "code",
+      "descField": "msg"
+    },
+    {
+      "title": "支付方式枚举",
+      "enumClassName": "com.chuanghai.smartschool.tuitionpayment.enume.PayMethod",
+      "codeField": "code",
+      "descField": "msg"
+    }
+  ],
+  "errorCodeDictionaries": [
+    {
+      "title": "业务异常代码",
+      "enumClassName": "com.chuanghai.smartschool.tuitionpayment.common.exception.BizCodeEnume",
+      "codeField": "code",
+      "descField": "msg"
+    }
+  ]
+}

Plik diff jest za duży
+ 7 - 0
src/main/resources/static/doc/AllInOne.css


Plik diff jest za duży
+ 1962 - 0
src/main/resources/static/doc/AllInOne.md


Plik diff jest za duży
+ 384 - 0
src/main/resources/static/doc/index.html


+ 327 - 0
src/main/resources/static/doc/search.js

@@ -0,0 +1,327 @@
+let api = [];
+api.push({
+    alias: 'AdminInfoController',
+    order: '1',
+    link: '管理员',
+    desc: '管理员',
+    list: []
+})
+api[0].list.push({
+    order: '1',
+    desc: '管理员登录',
+});
+api[0].list.push({
+    order: '2',
+    desc: '管理员列表',
+});
+api[0].list.push({
+    order: '3',
+    desc: '新增管理员',
+});
+api[0].list.push({
+    order: '4',
+    desc: '修改管理员',
+});
+api[0].list.push({
+    order: '5',
+    desc: '删除管理员',
+});
+api.push({
+    alias: 'EnablePayLimitByGradeController',
+    order: '2',
+    link: '年级可支付限制设置',
+    desc: '年级可支付限制设置',
+    list: []
+})
+api[1].list.push({
+    order: '1',
+    desc: '支付限制列表',
+});
+api[1].list.push({
+    order: '2',
+    desc: '支付限制设置',
+});
+api.push({
+    alias: 'FeedbackMsgController',
+    order: '3',
+    link: '反馈信息',
+    desc: '反馈信息',
+    list: []
+})
+api[2].list.push({
+    order: '1',
+    desc: '新增反馈信息',
+});
+api[2].list.push({
+    order: '2',
+    desc: '反馈列表【当前用户】',
+});
+api[2].list.push({
+    order: '3',
+    desc: '反馈信息删除',
+});
+api[2].list.push({
+    order: '4',
+    desc: '反馈列表【管理员】',
+});
+api[2].list.push({
+    order: '5',
+    desc: '反馈详细信息',
+});
+api[2].list.push({
+    order: '6',
+    desc: '反馈信息处理',
+});
+api.push({
+    alias: 'PayableInfoController',
+    order: '4',
+    link: '学生应缴数据',
+    desc: '学生应缴数据',
+    list: []
+})
+api[3].list.push({
+    order: '1',
+    desc: '根据card_number获取缴费信息',
+});
+api[3].list.push({
+    order: '2',
+    desc: '根据学号和学生姓名获取缴费信息',
+});
+api[3].list.push({
+    order: '3',
+    desc: '学生应缴列表',
+});
+api[3].list.push({
+    order: '4',
+    desc: '学生应缴信息删除',
+});
+api[3].list.push({
+    order: '5',
+    desc: 'excel 导入学生学费相关原始数据',
+});
+api[3].list.push({
+    order: '6',
+    desc: '删除缴费信息',
+});
+api.push({
+    alias: 'PayController',
+    order: '5',
+    link: '支付接口',
+    desc: '支付接口',
+    list: []
+})
+api[4].list.push({
+    order: '1',
+    desc: '【建行】获取拉起微信支付的参数',
+});
+api[4].list.push({
+    order: '2',
+    desc: '【建行】支付结果异步通知',
+});
+api[4].list.push({
+    order: '3',
+    desc: '【建行】支付成功回跳页面',
+});
+api[4].list.push({
+    order: '4',
+    desc: '【农商行】发起支付',
+});
+api[4].list.push({
+    order: '5',
+    desc: '【农商行】支付结果异步通知',
+});
+api[4].list.push({
+    order: '6',
+    desc: '【农商行】支付成功跳转地址',
+});
+api.push({
+    alias: 'PayMethodSettingController',
+    order: '6',
+    link: '支付方式设置',
+    desc: '支付方式设置',
+    list: []
+})
+api[5].list.push({
+    order: '1',
+    desc: '支付方式列表',
+});
+api[5].list.push({
+    order: '2',
+    desc: '设置 payMethodId 为当前支付方式',
+});
+api[5].list.push({
+    order: '3',
+    desc: '获取当前支付方式',
+});
+api.push({
+    alias: 'PayOrderController',
+    order: '7',
+    link: '缴费订单',
+    desc: '缴费订单',
+    list: []
+})
+api[6].list.push({
+    order: '1',
+    desc: '创建支付订单',
+});
+api[6].list.push({
+    order: '2',
+    desc: '订单列表【当前用户】',
+});
+api[6].list.push({
+    order: '3',
+    desc: '查询订单状态【用户端】',
+});
+api[6].list.push({
+    order: '4',
+    desc: '订单列表【管理端】',
+});
+api[6].list.push({
+    order: '5',
+    desc: '查询订单状态【管理端】',
+});
+api[6].list.push({
+    order: '6',
+    desc: '下载完成缴费的学生名单',
+});
+api.push({
+    alias: 'TestController',
+    order: '8',
+    link: '',
+    desc: '',
+    list: []
+})
+api[7].list.push({
+    order: '1',
+    desc: '',
+});
+api.push({
+    alias: 'WechatController',
+    order: '9',
+    link: '微信相关接口',
+    desc: '微信相关接口',
+    list: []
+})
+api[8].list.push({
+    order: '1',
+    desc: '微信授权回调地址',
+});
+api[8].list.push({
+    order: '2',
+    desc: '微校授权回调地址',
+});
+api.push({
+    alias: 'error',
+    order: '10',
+    link: 'error_code_list',
+    desc: '错误码列表',
+    list: []
+})
+api.push({
+    alias: 'dict',
+    order: '11',
+    link: 'dict_list',
+    desc: '数据字典',
+    list: []
+})
+api[10].list.push({
+    order: '1',
+    desc: '支付状态枚举',
+});
+api[10].list.push({
+    order: '2',
+    desc: '反馈信息状态枚举',
+});
+api[10].list.push({
+    order: '3',
+    desc: '支付方式枚举',
+});
+document.onkeydown = keyDownSearch;
+function keyDownSearch(e) {
+    const theEvent = e;
+    const code = theEvent.keyCode || theEvent.which || theEvent.charCode;
+    if (code == 13) {
+        const search = document.getElementById('search');
+        const searchValue = search.value;
+        let searchArr = [];
+        for (let i = 0; i < api.length; i++) {
+            let apiData = api[i];
+            const desc = apiData.desc;
+            if (desc.indexOf(searchValue) > -1) {
+                searchArr.push({
+                    order: apiData.order,
+                    desc: apiData.desc,
+                    link: apiData.link,
+                    list: apiData.list
+                });
+            } else {
+                let methodList = apiData.list || [];
+                let methodListTemp = [];
+                for (let j = 0; j < methodList.length; j++) {
+                    const methodData = methodList[j];
+                    const methodDesc = methodData.desc;
+                    if (methodDesc.indexOf(searchValue) > -1) {
+                        methodListTemp.push(methodData);
+                        break;
+                    }
+                }
+                if (methodListTemp.length > 0) {
+                    const data = {
+                        order: apiData.order,
+                        desc: apiData.desc,
+                        link: apiData.link,
+                        list: methodListTemp
+                    };
+                    searchArr.push(data);
+                }
+            }
+        }
+        let html;
+        if (searchValue == '') {
+            const liClass = "";
+            const display = "display: none";
+            html = buildAccordion(api,liClass,display);
+            document.getElementById('accordion').innerHTML = html;
+        } else {
+            const liClass = "open";
+            const display = "display: block";
+            html = buildAccordion(searchArr,liClass,display);
+            document.getElementById('accordion').innerHTML = html;
+        }
+        const Accordion = function (el, multiple) {
+            this.el = el || {};
+            this.multiple = multiple || false;
+            const links = this.el.find('.dd');
+            links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown);
+        };
+        Accordion.prototype.dropdown = function (e) {
+            const $el = e.data.el;
+            $this = $(this), $next = $this.next();
+            $next.slideToggle();
+            $this.parent().toggleClass('open');
+            if (!e.data.multiple) {
+                $el.find('.submenu').not($next).slideUp("20").parent().removeClass('open');
+            }
+        };
+        new Accordion($('#accordion'), false);
+    }
+}
+
+function buildAccordion(apiData, liClass, display) {
+    let html = "";
+    let doc;
+    if (apiData.length > 0) {
+        for (let j = 0; j < apiData.length; j++) {
+            html += '<li class="'+liClass+'">';
+            html += '<a class="dd" href="#_' + apiData[j].link + '">' + apiData[j].order + '.&nbsp;' + apiData[j].desc + '</a>';
+            html += '<ul class="sectlevel2" style="'+display+'">';
+            doc = apiData[j].list;
+            for (let m = 0; m < doc.length; m++) {
+                html += '<li><a href="#_' + apiData[j].order + '_' + doc[m].order + '_' + doc[m].desc + '">' + apiData[j].order + '.' + doc[m].order + '.&nbsp;' + doc[m].desc + '</a> </li>';
+            }
+            html += '</ul>';
+            html += '</li>';
+        }
+    }
+    return html;
+}

BIN
src/main/resources/static/doc/缴费系统接口文档.pdf


+ 13 - 0
src/test/java/com/chuanghai/smartschool/tuitionpayment/TuitionPaymentApplicationTests.java

@@ -0,0 +1,13 @@
+package com.chuanghai.smartschool.tuitionpayment;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class TuitionPaymentApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}