User 2 hete
commit
cb3d5f33ee
100 módosított fájl, 9669 hozzáadás és 0 törlés
  1. 2 0
      .gitignore
  2. 36 0
      README.en.md
  3. 39 0
      README.md
  4. 75 0
      package.xml
  5. 307 0
      pom.xml
  6. BIN
      src/.DS_Store
  7. BIN
      src/main/.DS_Store
  8. 3 0
      src/main/java/META-INF/MANIFEST.MF
  9. 16 0
      src/main/java/com/dtb/portal/PortalApplication.java
  10. 83 0
      src/main/java/com/dtb/portal/config/AliyunConfig.java
  11. 178 0
      src/main/java/com/dtb/portal/config/WxPayAppConfig.java
  12. 171 0
      src/main/java/com/dtb/portal/config/WxPayMiniprogramConfig.java
  13. 62 0
      src/main/java/com/dtb/portal/config/datasource/DataSourceConfig.java
  14. 21 0
      src/main/java/com/dtb/portal/config/interceptor/CodeEnum.java
  15. 39 0
      src/main/java/com/dtb/portal/config/interceptor/CodeSecurity.java
  16. 15 0
      src/main/java/com/dtb/portal/config/interceptor/LoginClassAnnotation.java
  17. 101 0
      src/main/java/com/dtb/portal/config/interceptor/LoginInterceptor.java
  18. 465 0
      src/main/java/com/dtb/portal/config/interceptor/RedisUtils.java
  19. 326 0
      src/main/java/com/dtb/portal/config/interceptor/RestParamInterceptor.java
  20. 79 0
      src/main/java/com/dtb/portal/config/interceptor/SensitiveWordsConfig.java
  21. 29 0
      src/main/java/com/dtb/portal/config/interceptor/TokenConfig.java
  22. 32 0
      src/main/java/com/dtb/portal/config/interceptor/TokenEnum.java
  23. 149 0
      src/main/java/com/dtb/portal/config/interceptor/TokenUtils.java
  24. 54 0
      src/main/java/com/dtb/portal/config/redis/RedisConfig.java
  25. 72 0
      src/main/java/com/dtb/portal/config/swagger/SwaggerConfig.java
  26. 98 0
      src/main/java/com/dtb/portal/constans/CommonConstant.java
  27. 68 0
      src/main/java/com/dtb/portal/constans/TokenConstants.java
  28. 43 0
      src/main/java/com/dtb/portal/constans/WxAppApiConstant.java
  29. 60 0
      src/main/java/com/dtb/portal/controller/FileController.java
  30. 220 0
      src/main/java/com/dtb/portal/controller/LoginController.java
  31. 25 0
      src/main/java/com/dtb/portal/controller/TestController.java
  32. 148 0
      src/main/java/com/dtb/portal/controller/UserController.java
  33. 222 0
      src/main/java/com/dtb/portal/controller/VideoController.java
  34. 66 0
      src/main/java/com/dtb/portal/controller/VideoUserController.java
  35. 54 0
      src/main/java/com/dtb/portal/controller/VipGoodsController.java
  36. 260 0
      src/main/java/com/dtb/portal/controller/WxPayController.java
  37. 73 0
      src/main/java/com/dtb/portal/controller/view/AppView.java
  38. 154 0
      src/main/java/com/dtb/portal/controller/view/RestResponse.java
  39. 59 0
      src/main/java/com/dtb/portal/controller/view/pageable/Page.java
  40. 41 0
      src/main/java/com/dtb/portal/controller/view/pageable/PageQuery.java
  41. 57 0
      src/main/java/com/dtb/portal/controller/view/request/UserAddByNamePassRequest.java
  42. 49 0
      src/main/java/com/dtb/portal/controller/view/request/UserAddRequest.java
  43. 16 0
      src/main/java/com/dtb/portal/controller/view/request/UserRemoveNotCheckRequest.java
  44. 27 0
      src/main/java/com/dtb/portal/controller/view/request/UserRemoveRequest.java
  45. 22 0
      src/main/java/com/dtb/portal/controller/view/request/UserRequest.java
  46. 55 0
      src/main/java/com/dtb/portal/controller/view/request/UserUpdateRequest.java
  47. 15 0
      src/main/java/com/dtb/portal/controller/view/request/VideoRequest.java
  48. 12 0
      src/main/java/com/dtb/portal/controller/view/request/VideoUserRequest.java
  49. 20 0
      src/main/java/com/dtb/portal/controller/view/request/VideoUserUpdateRequest.java
  50. 29 0
      src/main/java/com/dtb/portal/controller/view/request/VipGoodsRequest.java
  51. 21 0
      src/main/java/com/dtb/portal/controller/view/response/CountAndNameResponse.java
  52. 56 0
      src/main/java/com/dtb/portal/controller/view/response/ResultMap.java
  53. 102 0
      src/main/java/com/dtb/portal/controller/view/response/UserPoolView.java
  54. 27 0
      src/main/java/com/dtb/portal/controller/view/response/UserSecurityResponse.java
  55. 28 0
      src/main/java/com/dtb/portal/dto/VideoUploadDTO.java
  56. 49 0
      src/main/java/com/dtb/portal/entity/AppPayData.java
  57. 71 0
      src/main/java/com/dtb/portal/entity/MiniprogramPayData.java
  58. 76 0
      src/main/java/com/dtb/portal/entity/SysUser.java
  59. 42 0
      src/main/java/com/dtb/portal/entity/Video.java
  60. 17 0
      src/main/java/com/dtb/portal/entity/VideoUser.java
  61. 23 0
      src/main/java/com/dtb/portal/entity/VipGoodsData.java
  62. 35 0
      src/main/java/com/dtb/portal/entity/VipOrderData.java
  63. 37 0
      src/main/java/com/dtb/portal/entity/VipUserData.java
  64. 68 0
      src/main/java/com/dtb/portal/exception/GlobalException.java
  65. 113 0
      src/main/java/com/dtb/portal/exception/GlobalExceptionHandler.java
  66. 34 0
      src/main/java/com/dtb/portal/mapper/SysUserMapper.java
  67. 25 0
      src/main/java/com/dtb/portal/mapper/VideoMapper.java
  68. 16 0
      src/main/java/com/dtb/portal/mapper/VideoUserMapper.java
  69. 39 0
      src/main/java/com/dtb/portal/mapper/VipGoodsMapper.java
  70. 16 0
      src/main/java/com/dtb/portal/service/VideoUserService.java
  71. 33 0
      src/main/java/com/dtb/portal/service/VipGoodsService.java
  72. 74 0
      src/main/java/com/dtb/portal/service/WxPayService.java
  73. 860 0
      src/main/java/com/dtb/portal/service/impl/SysUserServiceImpl.java
  74. 75 0
      src/main/java/com/dtb/portal/service/impl/VideoServiceImpl.java
  75. 82 0
      src/main/java/com/dtb/portal/service/impl/VideoUserServiceImpl.java
  76. 119 0
      src/main/java/com/dtb/portal/service/impl/VipGoodsServiceImpl.java
  77. 925 0
      src/main/java/com/dtb/portal/service/impl/WxPayServiceImpl.java
  78. 269 0
      src/main/java/com/dtb/portal/util/AliyunOSSUtil.java
  79. 56 0
      src/main/java/com/dtb/portal/util/AliyunSMSUtil.java
  80. 148 0
      src/main/java/com/dtb/portal/util/CodeDefault.java
  81. 23 0
      src/main/java/com/dtb/portal/util/CodeEnum.java
  82. 123 0
      src/main/java/com/dtb/portal/util/DateUtils.java
  83. 48 0
      src/main/java/com/dtb/portal/util/HttpClientUtils.java
  84. 107 0
      src/main/java/com/dtb/portal/util/MobileUtil.java
  85. 41 0
      src/main/java/com/dtb/portal/util/PrimaryIdUtils.java
  86. 30 0
      src/main/java/com/dtb/portal/util/RandomUtil.java
  87. 386 0
      src/main/java/com/dtb/portal/util/SendSms.java
  88. 35 0
      src/main/java/com/dtb/portal/util/StrengthUtil.java
  89. 48 0
      src/main/java/com/dtb/portal/util/TokenEnum.java
  90. 113 0
      src/main/java/com/dtb/portal/util/ValidateCodeImageUtils.java
  91. 162 0
      src/main/java/com/dtb/portal/util/WxAppApiUtils.java
  92. 210 0
      src/main/java/com/dtb/portal/util/WxPhoneNumberUtils.java
  93. 129 0
      src/main/resources/application.properties
  94. 145 0
      src/main/resources/mybatis/SysUserMapper.xml
  95. 69 0
      src/main/resources/mybatis/VideoMapper.xml
  96. 47 0
      src/main/resources/mybatis/VideoUserMapper.xml
  97. 123 0
      src/main/resources/mybatis/VipGoodsMapper.xml
  98. 21 0
      src/main/resources/vip表设计
  99. 13 0
      src/main/resources/需求
  100. 13 0
      src/test/java/com/dtb/portal/sdaasportal/ApplicationTests.java

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+.idea
+__MACOSX

+ 36 - 0
README.en.md

@@ -0,0 +1,36 @@
+# userpay
+
+#### Description
+{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
+
+#### Software Architecture
+Software architecture description
+
+#### Installation
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### Instructions
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### Contribution
+
+1.  Fork the repository
+2.  Create Feat_xxx branch
+3.  Commit your code
+4.  Create Pull Request
+
+
+#### Gitee Feature
+
+1.  You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
+2.  Gitee blog [blog.gitee.com](https://blog.gitee.com)
+3.  Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
+4.  The most valuable open source project [GVP](https://gitee.com/gvp)
+5.  The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
+6.  The most popular members  [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 39 - 0
README.md

@@ -0,0 +1,39 @@
+# userpay
+
+#### 介绍
+{**以下是 Gitee 平台说明,您可以替换此简介**
+Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
+无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
+
+#### 软件架构
+软件架构说明
+
+
+#### 安装教程
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### 使用说明
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### 参与贡献
+
+1.  Fork 本仓库
+2.  新建 Feat_xxx 分支
+3.  提交代码
+4.  新建 Pull Request
+
+
+#### 特技
+
+1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
+2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
+3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
+4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
+5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
+6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 75 - 0
package.xml

@@ -0,0 +1,75 @@
+<assembly>
+    <id>bin</id>
+    <!-- 最终打包成一个用于发布的zip文件 -->
+    <formats>
+        <format>tar.gz</format>
+    </formats>
+
+    <!-- Adds dependencies to zip package under lib directory -->
+    <dependencySets>
+        <!-- 不使用项目的artifact,第三方jar不要解压,打包进zip文件的lib目录 -->
+        <dependencySet>
+            <useProjectArtifact>false</useProjectArtifact>
+            <outputDirectory>lib</outputDirectory>
+            <unpack>false</unpack>
+        </dependencySet>
+        <!-- 增加scope类型为system的配置 -->
+        <dependencySet>
+            <useProjectArtifact>false</useProjectArtifact>
+            <outputDirectory>/lib</outputDirectory>
+            <unpack>false</unpack>
+            <scope>system</scope>
+        </dependencySet>
+    </dependencySets>
+
+    <fileSets>
+        <!-- 把项目相关的说明文件,打包进zip文件的根目录 -->
+        <fileSet>
+            <directory>${project.basedir}</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>README*</include>
+                <include>LICENSE*</include>
+                <include>NOTICE*</include>
+            </includes>
+        </fileSet>
+
+        <!-- 把项目的配置文件,打包进zip文件的config目录 -->
+        <fileSet>
+            <directory>${project.basedir}/src/main/resources/</directory>
+            <outputDirectory>config</outputDirectory>
+            <includes>
+                <include>*.properties</include>
+                <include>*.yml</include>
+                <include>*.yaml</include>
+                <include>*.xml</include>
+                <include>*.json</include>
+            </includes>
+        </fileSet>
+        <!-- 把项目脚本,打包进zip文件的script目录 -->
+        <fileSet>
+            <directory>${project.basedir}/src/main/bin/</directory>
+            <outputDirectory>bin</outputDirectory>
+            <includes>
+                <include>start.sh</include>
+                <include>stop.sh</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/src/main/bin/</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>run.sh</include>
+            </includes>
+        </fileSet>
+
+        <!-- 把项目自己编译出来的jar文件,打包进zip文件的根目录 -->
+        <fileSet>
+            <directory>${project.build.directory}</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>${project.artifactId}.jar</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</assembly>

+ 307 - 0
pom.xml

@@ -0,0 +1,307 @@
+<?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.1.12.RELEASE</version>-->
+		<version>2.1.11.RELEASE</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+	<groupId>com.users</groupId>
+	<artifactId>users_pay</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>users_pay</name>
+	<description>users_pay</description>
+	<properties>
+		<java.version>8</java.version>
+		<pagehelper.version>1.4.1</pagehelper.version>
+		<druid.version>1.2.8</druid.version>
+		<mybatis.version>2.2.0</mybatis.version>
+		<swagger.version>2.9.2</swagger.version>
+		<jwt.version>3.18.2</jwt.version>
+		<feign.version>2.1.4.RELEASE</feign.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-starter-aop</artifactId>
+		</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>
+
+		<!--集成打印日志-->
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+			<version>3.1</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.github.pagehelper</groupId>
+			<artifactId>pagehelper-spring-boot-starter</artifactId>
+			<version>${pagehelper.version}</version>
+			<exclusions>
+				<exclusion>
+					<artifactId>mybatis-spring-boot-starter</artifactId>
+					<groupId>org.mybatis.spring.boot</groupId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+
+		<dependency>
+			<groupId>org.mybatis.spring.boot</groupId>
+			<artifactId>mybatis-spring-boot-starter</artifactId>
+			<version>${mybatis.version}</version>
+		</dependency>
+
+
+		<!-- 集成druid连接池-->
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid-spring-boot-starter</artifactId>
+			<version>${druid.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<scope>runtime</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>io.springfox</groupId>
+			<artifactId>springfox-swagger-ui</artifactId>
+			<version>${swagger.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>io.springfox</groupId>
+			<artifactId>springfox-swagger2</artifactId>
+			<version>${swagger.version}</version>
+		</dependency>
+		<!--  <dependency>
+              <groupId>com.auth0</groupId>
+              <artifactId>java-jwt</artifactId>
+              <version>${jwt.version}</version>
+          </dependency>-->
+		<!--集成redis-->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+			<version>1.2.76</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.github.wxpay</groupId>
+			<artifactId>wxpay-sdk</artifactId>
+			<version>0.0.3</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.github.wechatpay-apiv3</groupId>
+			<artifactId>wechatpay-java</artifactId>
+			<version>0.2.12</version>
+			<exclusions>
+				<exclusion>
+					<!-- 过滤okhttp包-->
+					<groupId>com.squareup.okhttp3</groupId>
+					<artifactId>okhttp</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>com.squareup.okhttp3</groupId>
+			<artifactId>okhttp</artifactId>
+			<version>3.4.2</version>
+		</dependency>
+
+		<!--<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpcore</artifactId>
+			<version>4.4.10</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpclient</artifactId>
+			<version>4.5.6</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpmime</artifactId>
+			<version>4.5</version>
+		</dependency>-->
+
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt</artifactId>
+			<version>0.9.1</version>
+		</dependency>
+		<dependency>
+			<groupId>javax.xml.bind</groupId>
+			<artifactId>jaxb-api</artifactId>
+			<version>2.3.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.glassfish.jaxb</groupId>
+			<artifactId>jaxb-runtime</artifactId>
+			<version>2.3.1</version>
+		</dependency>
+        <!-- 阿里云短信服务依赖 -->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>4.5.20</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
+            <version>2.1.0</version>
+        </dependency>
+        <!-- 阿里云 OSS 对象存储依赖 -->
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.16.2</version>
+        </dependency>
+	</dependencies>
+
+
+
+
+	<build>
+		<finalName>${project.artifactId}</finalName>
+		<resources>
+			<resource>
+				<directory>src/main/resources</directory>
+				<includes>
+					<include>**/*.yml</include>
+					<include>**/*.yaml</include>
+					<include>**/*.properties</include>
+					<include>**/*.xml</include>
+				</includes>
+				<filtering>false</filtering>
+			</resource>
+		</resources>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<addMavenDescriptor>false</addMavenDescriptor>
+						<manifest>
+							<addClasspath>true</addClasspath>
+							<classpathPrefix>lib/</classpathPrefix>
+							<mainClass>com.dtb.portal.PortalApplication</mainClass>
+						</manifest>
+						<manifestEntries>
+							<Class-Path>config/</Class-Path>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-resources-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>copy-resources</id>
+						<phase>package</phase>
+						<goals>
+							<goal>copy-resources</goal>
+						</goals>
+						<configuration>
+							<outputDirectory>${project.build.directory}/maven-archiver/resources</outputDirectory>
+							<resources>
+								<resource>
+									<directory>${basedir}/src/main/resources</directory>
+									<include>*.properties</include>
+									<filtering>true</filtering>
+								</resource>
+							</resources>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+					<descriptors>
+						<descriptor>${project.basedir}/package.xml</descriptor>
+					</descriptors>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-antrun-plugin</artifactId>
+				<version>1.8</version>
+				<executions>
+					<execution>
+						<id>deploy</id>
+						<phase>package</phase>
+						<goals>
+							<goal>run</goal>
+						</goals>
+						<configuration>
+							<target>
+								<untar src="${project.build.directory}/${project.artifactId}-bin.tar.gz"
+									   dest="${project.build.directory}" overwrite="true" compression="gzip"/>
+							</target>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>8</source>
+					<target>8</target>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

BIN
src/.DS_Store


BIN
src/main/.DS_Store


+ 3 - 0
src/main/java/META-INF/MANIFEST.MF

@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: com.dtb.portal.PortalApplication
+

+ 16 - 0
src/main/java/com/dtb/portal/PortalApplication.java

@@ -0,0 +1,16 @@
+package com.dtb.portal;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+
+@SpringBootApplication
+public class PortalApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(PortalApplication.class, args);
+    }
+
+
+
+}

+ 83 - 0
src/main/java/com/dtb/portal/config/AliyunConfig.java

@@ -0,0 +1,83 @@
+package com.dtb.portal.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author:slambb
+ * @date:2019/12/16
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "aliyun")
+public class AliyunConfig {
+    //公共
+    private String accessKeyId;
+    private String AccessKeySecret;
+    //oss 服务
+    private Boolean usingDomain;
+    private String domainName;
+    private String endpoint;
+    private String bucketName;
+    private String fileHost;
+    private String fileAvatar;
+    private String fileImages;
+    private String filePublicAvatar;
+    private String fileGameIcon;
+    private String filePictures;
+    private String fileVideos;
+    //短信服务
+    private String regionId;
+    private String signName;
+    private String templateCode;
+    
+    /**
+     * 获取签名名称,处理编码问题
+     */
+    public String getSignName() {
+        if (signName == null) {
+            return null;
+        }
+        
+        // 如果是Unicode编码,进行解码
+        if (signName.contains("\\u")) {
+            try {
+                return java.net.URLDecoder.decode(signName, "UTF-8");
+            } catch (Exception e) {
+                // 如果解码失败,尝试直接转换
+                try {
+                    return new String(signName.getBytes("ISO-8859-1"), "UTF-8");
+                } catch (Exception ex) {
+                    return signName;
+                }
+            }
+        }
+        
+        // 尝试多种编码方式处理
+        try {
+            // 首先尝试UTF-8
+            return new String(signName.getBytes("UTF-8"), "UTF-8");
+        } catch (Exception e1) {
+            try {
+                // 然后尝试ISO-8859-1转UTF-8
+                return new String(signName.getBytes("ISO-8859-1"), "UTF-8");
+            } catch (Exception e2) {
+                try {
+                    // 最后尝试GBK
+                    return new String(signName.getBytes("GBK"), "UTF-8");
+                } catch (Exception e3) {
+                    return signName;
+                }
+            }
+        }
+    }
+    
+    /**
+     * 测试方法:打印签名名称用于调试
+     */
+    public void printSignName() {
+        System.out.println("原始signName: " + signName);
+        System.out.println("处理后的signName: " + getSignName());
+    }
+}

+ 178 - 0
src/main/java/com/dtb/portal/config/WxPayAppConfig.java

@@ -0,0 +1,178 @@
+package com.dtb.portal.config;
+
+import com.github.wxpay.sdk.WXPayConfig;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.io.InputStream;
+
+/**
+ * 配置我们自己的信息
+ */
+
+@Component
+@ConfigurationProperties(prefix = "wxpay.app")
+public class WxPayAppConfig implements WXPayConfig {
+    /**
+     * appID
+     */
+    private String appID;
+
+    /**
+     * 商户号
+     */
+    private String mchID;
+
+    /**
+     * API 密钥
+     */
+    private String key;
+
+
+    /**
+     * API证书绝对路径 (本项目放在了 resources/cert/wxpay/apiclient_cert.p12")
+     */
+    private String certPath;
+
+    /**
+     * HTTP(S) 连接超时时间,单位毫秒
+     */
+    private int httpConnectTimeoutMs = 8000;
+
+    /**
+     * HTTP(S) 读数据超时时间,单位毫秒
+     */
+    private int httpReadTimeoutMs = 10000;
+
+    /**
+     * 微信支付异步通知地址
+     */
+    private String payNotifyUrl;
+
+    /**
+     * 微信退款异步通知地址
+     */
+    private String refundNotifyUrl;
+
+
+    /**
+     * 证书序列号
+     */
+    private String merchantSerialNumber;
+
+    /**
+     * V3密钥
+     */
+    private String v3Key;
+
+
+    /**
+     * 获取商户证书内容(这里证书需要到微信商户平台进行下载)
+     *
+     * @return 商户证书内容
+     */
+    @Override
+    public InputStream getCertStream() {
+        return getClass().getClassLoader().getResourceAsStream(certPath);
+    }
+
+    public String getAppID() {
+        return appID;
+    }
+
+    public void setAppID(String appID) {
+        this.appID = appID;
+    }
+
+    public String getMchID() {
+        return mchID;
+    }
+
+    public void setMchID(String mchID) {
+        this.mchID = mchID;
+    }
+
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getCertPath() {
+        return certPath;
+    }
+
+    public void setCertPath(String certPath) {
+        this.certPath = certPath;
+    }
+
+    public int getHttpConnectTimeoutMs() {
+        return httpConnectTimeoutMs;
+    }
+
+    public void setHttpConnectTimeoutMs(int httpConnectTimeoutMs) {
+        this.httpConnectTimeoutMs = httpConnectTimeoutMs;
+    }
+
+    public int getHttpReadTimeoutMs() {
+        return httpReadTimeoutMs;
+    }
+
+    public void setHttpReadTimeoutMs(int httpReadTimeoutMs) {
+        this.httpReadTimeoutMs = httpReadTimeoutMs;
+    }
+
+    public String getPayNotifyUrl() {
+        return payNotifyUrl;
+    }
+
+    public void setPayNotifyUrl(String payNotifyUrl) {
+        this.payNotifyUrl = payNotifyUrl;
+    }
+
+    public String getRefundNotifyUrl() {
+        return refundNotifyUrl;
+    }
+
+    public void setRefundNotifyUrl(String refundNotifyUrl) {
+        this.refundNotifyUrl = refundNotifyUrl;
+    }
+
+    public String getMerchantSerialNumber() {
+        return merchantSerialNumber;
+    }
+
+    public void setMerchantSerialNumber(String merchantSerialNumber) {
+        this.merchantSerialNumber = merchantSerialNumber;
+    }
+
+    public String getV3Key() {
+        return v3Key;
+    }
+
+    public void setV3Key(String v3Key) {
+        this.v3Key = v3Key;
+    }
+
+
+
+    @Override
+    public String toString() {
+        return "WxPayAppConfig{" +
+                "appID='" + appID + '\'' +
+                ", mchID='" + mchID + '\'' +
+                ", key='" + key + '\'' +
+                ", certPath='" + certPath + '\'' +
+                ", httpConnectTimeoutMs=" + httpConnectTimeoutMs +
+                ", httpReadTimeoutMs=" + httpReadTimeoutMs +
+                ", payNotifyUrl='" + payNotifyUrl + '\'' +
+                ", refundNotifyUrl='" + refundNotifyUrl + '\'' +
+                ", merchantSerialNumber='" + merchantSerialNumber + '\'' +
+                ", v3Key='" + v3Key + '\'' +
+                '}';
+    }
+}

+ 171 - 0
src/main/java/com/dtb/portal/config/WxPayMiniprogramConfig.java

@@ -0,0 +1,171 @@
+package com.dtb.portal.config;
+
+import com.github.wxpay.sdk.WXPayConfig;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.io.InputStream;
+
+/**
+ * 微信小程序支付配置
+ */
+@Component
+@ConfigurationProperties(prefix = "wxpay.miniprogram")
+public class WxPayMiniprogramConfig implements WXPayConfig {
+    /**
+     * 小程序appID
+     */
+    private String appID;
+
+    /**
+     * 商户号
+     */
+    private String mchID;
+
+    /**
+     * API 密钥
+     */
+    private String key;
+
+    /**
+     * API证书绝对路径
+     */
+    private String certPath;
+
+    /**
+     * HTTP(S) 连接超时时间,单位毫秒
+     */
+    private int httpConnectTimeoutMs = 8000;
+
+    /**
+     * HTTP(S) 读数据超时时间,单位毫秒
+     */
+    private int httpReadTimeoutMs = 10000;
+
+    /**
+     * 微信支付异步通知地址
+     */
+    private String payNotifyUrl;
+
+    /**
+     * 微信退款异步通知地址
+     */
+    private String refundNotifyUrl;
+
+    /**
+     * 证书序列号
+     */
+    private String merchantSerialNumber;
+
+    /**
+     * V3密钥
+     */
+    private String v3Key;
+
+    /**
+     * 获取商户证书内容(这里证书需要到微信商户平台进行下载)
+     *
+     * @return 商户证书内容
+     */
+    @Override
+    public InputStream getCertStream() {
+        return getClass().getClassLoader().getResourceAsStream(certPath);
+    }
+
+    public String getAppID() {
+        return appID;
+    }
+
+    public void setAppID(String appID) {
+        this.appID = appID;
+    }
+
+    public String getMchID() {
+        return mchID;
+    }
+
+    public void setMchID(String mchID) {
+        this.mchID = mchID;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getCertPath() {
+        return certPath;
+    }
+
+    public void setCertPath(String certPath) {
+        this.certPath = certPath;
+    }
+
+    public int getHttpConnectTimeoutMs() {
+        return httpConnectTimeoutMs;
+    }
+
+    public void setHttpConnectTimeoutMs(int httpConnectTimeoutMs) {
+        this.httpConnectTimeoutMs = httpConnectTimeoutMs;
+    }
+
+    public int getHttpReadTimeoutMs() {
+        return httpReadTimeoutMs;
+    }
+
+    public void setHttpReadTimeoutMs(int httpReadTimeoutMs) {
+        this.httpReadTimeoutMs = httpReadTimeoutMs;
+    }
+
+    public String getPayNotifyUrl() {
+        return payNotifyUrl;
+    }
+
+    public void setPayNotifyUrl(String payNotifyUrl) {
+        this.payNotifyUrl = payNotifyUrl;
+    }
+
+    public String getRefundNotifyUrl() {
+        return refundNotifyUrl;
+    }
+
+    public void setRefundNotifyUrl(String refundNotifyUrl) {
+        this.refundNotifyUrl = refundNotifyUrl;
+    }
+
+    public String getMerchantSerialNumber() {
+        return merchantSerialNumber;
+    }
+
+    public void setMerchantSerialNumber(String merchantSerialNumber) {
+        this.merchantSerialNumber = merchantSerialNumber;
+    }
+
+    public String getV3Key() {
+        return v3Key;
+    }
+
+    public void setV3Key(String v3Key) {
+        this.v3Key = v3Key;
+    }
+
+    @Override
+    public String toString() {
+        return "WxPayMiniprogramConfig{" +
+                "appID='" + appID + '\'' +
+                ", mchID='" + mchID + '\'' +
+                ", key='" + key + '\'' +
+                ", certPath='" + certPath + '\'' +
+                ", httpConnectTimeoutMs=" + httpConnectTimeoutMs +
+                ", httpReadTimeoutMs=" + httpReadTimeoutMs +
+                ", payNotifyUrl='" + payNotifyUrl + '\'' +
+                ", refundNotifyUrl='" + refundNotifyUrl + '\'' +
+                ", merchantSerialNumber='" + merchantSerialNumber + '\'' +
+                ", v3Key='" + v3Key + '\'' +
+                '}';
+    }
+}

+ 62 - 0
src/main/java/com/dtb/portal/config/datasource/DataSourceConfig.java

@@ -0,0 +1,62 @@
+package com.dtb.portal.config.datasource;
+
+
+import com.alibaba.druid.pool.DruidDataSource;
+import org.apache.ibatis.io.VFS;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.annotation.MapperScan;
+import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+
+import javax.sql.DataSource;
+
+@Configuration
+@MapperScan(basePackages = DataSourceConfig.PACKAGE, sqlSessionTemplateRef = "masterSqlSessionTemplate")
+public class DataSourceConfig {
+    static final String PACKAGE = "com.dtb.portal.mapper";
+    static final String MAPPER_LOCATION = "classpath:mybatis/**/*.xml";
+
+    @Bean(name = "masterDatasource")
+    @ConfigurationProperties(prefix = "spring.datasource")
+    public DataSource druidDataSource() {
+        return new DruidDataSource();
+    }
+
+
+    @Bean(name = "masterSqlSessionFactory")
+    @Primary
+    @ConfigurationProperties(prefix = "spring.datasource")
+    public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDatasource") DataSource dataSource) throws Exception {
+        VFS.addImplClass(SpringBootVFS.class);//解决打包成JAR后 无法扫描实体类问题
+        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
+        sessionFactoryBean.setDataSource(dataSource);
+        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
+                .getResources(MAPPER_LOCATION));
+        sessionFactoryBean.setTypeAliasesPackage("com.dtb.portal.entity");
+        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
+        configuration.setMapUnderscoreToCamelCase(true);
+        sessionFactoryBean.setConfiguration(configuration);
+        return sessionFactoryBean.getObject();
+    }
+
+
+    @Bean(name = "masterTransactionManager")
+    @Primary
+    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("masterDatasource") DataSource dataSource) {
+        return new DataSourceTransactionManager(dataSource);
+    }
+
+    @Bean(name = "masterSqlSessionTemplate")
+    @Primary
+    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
+        return new SqlSessionTemplate(sqlSessionFactory);
+    }
+}

+ 21 - 0
src/main/java/com/dtb/portal/config/interceptor/CodeEnum.java

@@ -0,0 +1,21 @@
+package com.dtb.portal.config.interceptor;
+
+/**
+ * @author :mosence
+ * @date :2019/12/30
+ */
+public interface CodeEnum {
+    /**
+     * 消息代码
+     * 注意不要设计出越界CODE(long类型)
+     * [-9223372036854775808,9223372036854775807]
+     * @return 消息代码
+     */
+    long getCode();
+
+    /**
+     * 默认消息内容
+     * @return 默认消息内容
+     */
+    String getDefaultMessage();
+}

+ 39 - 0
src/main/java/com/dtb/portal/config/interceptor/CodeSecurity.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 eKing Technology, Inc. All Rights Reserved.
+ */
+package com.dtb.portal.config.interceptor;
+
+
+import com.google.common.base.MoreObjects;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum CodeSecurity implements CodeEnum {
+   /**
+    * 安全检查异常枚举
+    */
+   LLEGAL_KEYWORDS(100090001, "请求包含非法字符"),
+   AUTH_FAIL(100090002, "权限校验失败"),
+   NOT_LOGIN(100090003, "未登录"),
+   CHECK_FAIL(100090004, "验证失败,请重新登录后使用"),
+   TOKEN_EXPIRE(100090005, "TOKEN已过期"),
+   SESSION_EXPIRE(100090006, "会话超时,请重新登录"),
+   ;
+
+   // 返回客户端的编码
+   private final long code;
+
+   // 默认消息
+   private final String defaultMessage;
+
+   @Override
+   public String toString() {
+      return MoreObjects.toStringHelper(this)
+              .add("code", code)
+              .add("defaultMessage", defaultMessage)
+              .toString();
+   }
+
+}

+ 15 - 0
src/main/java/com/dtb/portal/config/interceptor/LoginClassAnnotation.java

@@ -0,0 +1,15 @@
+package com.dtb.portal.config.interceptor;
+
+import java.lang.annotation.*;
+
+/**
+ * 登录类注解
+ * @author: xumj
+ * @since: 2021-10-23
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface LoginClassAnnotation {
+
+}

+ 101 - 0
src/main/java/com/dtb/portal/config/interceptor/LoginInterceptor.java

@@ -0,0 +1,101 @@
+package com.dtb.portal.config.interceptor;
+
+
+import com.dtb.portal.exception.GlobalException;
+import io.jsonwebtoken.Claims;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import java.util.concurrent.TimeUnit;
+
+@Aspect
+@Component
+@Slf4j
+@Order(1)
+public class LoginInterceptor {
+
+    private static String header = "Authorization";
+
+    private static long redisExpire = 7200L;
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    /**
+     * 定义切入点,对要拦截的方法进行定义与限制,如包、类、方法
+     * 本次以controller.rest包下的全部的controloler类为点
+     */
+
+    @Pointcut("execution(* com.dtb.portal.controller.*.*(..))")
+    public void loginClassAspect() {
+    }
+
+    /**
+     * 环绕通知: 调用loginClassAspect()
+     */
+
+    @Around("loginClassAspect()")
+    public Object interceptException(ProceedingJoinPoint joinPoint) throws Throwable {
+        String userId = "";
+
+        // 判断controller类没有LoginClassAnnotation注解,没有该注解需要做权限验证
+        if (!joinPoint.getTarget().getClass().isAnnotationPresent(LoginClassAnnotation.class)) {
+            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+            String contextPath = request.getRequestURI();
+            String token = request.getHeader(header);
+            log.info("请求URI : " + contextPath + ", token : " + token);
+            //不拦截的路径
+            if (IGNORE_PATHS.contains(contextPath)) {
+                return joinPoint.proceed();
+            }
+            // 判断请求是否携带token
+            if (StringUtils.isBlank(token)) {
+                throw new RuntimeException("未登录");
+            }
+
+            // JWT解密验证
+            String originalToken = TokenUtils.getOriginalToken(token);
+            Claims claims = TokenUtils.getClaimsFromToken(originalToken);
+            if (Objects.isNull(claims)) {
+//                throw new GlobalException(CodeSecurity.CHECK_FAIL, null);
+                throw new RuntimeException("验证失败,请重新登录后使用");
+            }
+
+            // 验证Redis上缓存的token
+            userId = String.valueOf(claims.get(TokenEnum.TOKEN_USER_ID.getMessage()));
+            if (!redisTemplate.hasKey(userId)) {
+                throw new RuntimeException("会话超时,请重新登录");
+            }
+            //如果所有的验证都通过,刷新redis上的缓存过期时间(暂时,后续考虑做refresh_token)
+            redisTemplate.expire(userId, redisExpire, TimeUnit.SECONDS);
+        }
+        return joinPoint.proceed();
+    }
+
+    public static final List<String> IGNORE_PATHS;
+
+    static {
+        IGNORE_PATHS = new ArrayList<>();
+        IGNORE_PATHS.add("/api/file/upload");
+        IGNORE_PATHS.add("/api/user/add");
+        IGNORE_PATHS.add("/api/user/hello");
+        IGNORE_PATHS.add("/api/user/findByOpenId");
+        IGNORE_PATHS.add("/api/user/addByNamePass");
+        IGNORE_PATHS.add("/api/video/list");
+    }
+
+}

+ 465 - 0
src/main/java/com/dtb/portal/config/interceptor/RedisUtils.java

@@ -0,0 +1,465 @@
+package com.dtb.portal.config.interceptor;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Date: 2022/4/6 15:12
+ * @Author: xiuer
+ * @Describe: Redis工具类
+ */
+@Component
+public class RedisUtils {
+
+    @Autowired
+    private static RedisTemplate<String, Object> redisTemplate;
+
+    public RedisUtils(RedisTemplate<String, Object> redisTemplate){
+        this.redisTemplate = redisTemplate;
+    }
+
+    /**
+     * 指定缓存失效时间
+     * @param key  键
+     * @param time 时间(秒)
+     */
+    public static boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public static long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public static boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     * @param key 可以传一个值 或多个
+     */
+    public static void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    /**
+     * 普通缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public static Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 普通缓存放入
+     * @param key   键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public static boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public static boolean set(String key, Object value, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time,
+                        TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 递增
+     * @param key   键
+     * @param delta 要增加几(大于0)
+     */
+    public static long incr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     * @param key   键
+     * @param delta 要减少几(小于0)
+     */
+    public static long decr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+// ================================Map=================================
+
+    /**
+     * HashGet
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     */
+    public static Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public static Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     * @param key 键
+     * @param map 对应多个键值
+     */
+    public static boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     * @param key  键
+     * @param map  对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public static boolean hmset(String key, Map<String, Object> map, long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public static boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public static boolean hset(String key, String item, Object value, long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     * @param key  键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public static void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public static boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     * @param key  键
+     * @param item 项
+     * @param by   要增加几(大于0)
+     */
+    public static double hincr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     * @param key  键
+     * @param item 项
+     * @param by   要减少记(小于0)
+     */
+    public static double hdecr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+// ============================set=============================
+
+    /**
+     * 根据key获取Set中的所有值
+     * @param key 键
+     */
+    public static Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     * @param key   键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public static boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public static long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 将set数据放入缓存
+     * @param key    键
+     * @param time   时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public static long sSetAndTime(String key, long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0)
+                expire(key, time);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+
+    /**
+     * 通过索引 获取list中的值
+     * @param key   键
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0
+     *              时,-1,表尾,-2倒数第二个元素,依次类推
+     */
+    public static Object lGetIndex(String key, long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key   键
+     * @param value 值
+     */
+    public static boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     */
+    public static boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0)
+                expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key   键
+     * @param value 值
+     * @return
+     */
+    public static boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public static boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0)
+                expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     * @param key   键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public static boolean lUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     * @param key   键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public static long lRemove(String key, long count, Object value) {
+        try {
+            Long remove = redisTemplate.opsForList().remove(key, count, value);
+            return remove;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+}

+ 326 - 0
src/main/java/com/dtb/portal/config/interceptor/RestParamInterceptor.java

@@ -0,0 +1,326 @@
+package com.dtb.portal.config.interceptor;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import com.dtb.portal.exception.GlobalException;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+
+@Aspect
+@Component
+@Slf4j
+@Order(0)
+public class RestParamInterceptor {
+
+
+    @Autowired
+    private HttpServletRequest request;
+
+
+/*    @Autowired
+    private SysLogService sysLogService;*/
+
+    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
+    public void anyControllerPointcut() {
+    }
+
+    /**
+     * 这个要分离成一个新的 Pointcut
+     */
+    @Pointcut("execution(@org.springframework.web.bind.annotation.RequestMapping * *(..))" +
+            " || execution(@org.springframework.web.bind.annotation.GetMapping * *(..))" +
+            " || execution(@org.springframework.web.bind.annotation.PostMapping * *(..))" +
+            " || execution(@org.springframework.web.bind.annotation.PutMapping * *(..))" +
+            " || execution(@org.springframework.web.bind.annotation.DeleteMapping * *(..))")
+    public void anyMethodPointcut() {
+    }
+
+
+    @Around("anyControllerPointcut() && anyMethodPointcut()")
+    public Object interceptException(ProceedingJoinPoint joinPoint) throws Throwable {
+
+        long startTime = System.currentTimeMillis();
+
+        Signature signature = joinPoint.getSignature();
+
+        // 把参数拼接出来
+        String[] paramNames = ((MethodSignature) signature).getParameterNames();
+
+        Object[] paramValues = joinPoint.getArgs();
+        Validate.isTrue(paramValues.length == paramNames.length);
+
+        Map<String, Object> name2Values = Maps.newHashMap();
+        Map<String, Object> name2ValueObjects = Maps.newHashMap();
+        for (int i = 0; i < paramNames.length; i++) {
+
+            if (paramValues[i] instanceof ServletRequest || paramValues[i] instanceof ServletResponse || paramValues[i] instanceof MultipartFile) {
+                continue;
+            }
+            name2Values.put(paramNames[i], paramValue2String(paramValues[i]));
+            name2ValueObjects.put(paramNames[i], paramValues[i]);
+        }
+
+        String uuid = UUID.randomUUID().toString();
+        // 真正方法调用
+        String args = Joiner.on(';').withKeyValueSeparator(":").join(name2Values);
+        log.info("Start action: {}, uuid: {}, Args => {}", signature.toShortString(), uuid, args);
+
+//        // 参数校验
+//        this.checkIllegalCharacter(name2ValueObjects, uuid);
+
+        Object returnValue = joinPoint.proceed();
+        // 先不输出 returnValue,因为数据量太大
+        //log.info("Finish action: {}, uuid: {}, returnValue: {}", signature.toShortString(), uuid);
+        long endTime = System.currentTimeMillis();
+        String consumeTime = (endTime - startTime) / 60 + "";
+        //写入日志表
+     /*   SysLogData sysLogData = new SysLogData();
+        long id = SpringContextUtils.getContext().getBean(SnowFlakeUtils.class).nextId();*/
+
+        //获取具体的方法
+        String fullClassName = joinPoint.getTarget().getClass().getName();
+        String methodName = signature.getName();
+
+        //获取注解的属性值
+        ApiOperation annotation = ((MethodSignature) signature).getMethod().getAnnotation(ApiOperation.class);
+        try {
+           // sysLogData.setParam(JSON.toJSONString(name2ValueObjects));
+        } catch (Exception e) {
+            log.error("name2ValueObjects is:{}", name2ValueObjects);
+            log.error("捕获参数异常:",e);
+           // sysLogData.setParam(null);
+        } finally {
+            String ip = getIp(request);
+         /*   sysLogData.setId(String.valueOf(id));
+            sysLogData.setUsername(UserUtils.getCurrentUser().getAccount());
+            sysLogData.setIp(ip);
+            sysLogData.setConsumeTime(consumeTime);
+            sysLogData.setMethodName(String.format("%s.%s", fullClassName, methodName));
+            sysLogData.setDescription(Optional.ofNullable(annotation).map(ApiOperation::value).orElse(""));
+            sysLogData.setCreateBy(UserUtils.getCurrentUser().getAccount());
+            sysLogData.setCreateTime(new Date());
+            sysLogService.save(sysLogData);*/
+        }
+
+
+        return returnValue;
+    }
+
+    /**
+     * 获取IP地址
+     *
+     * @param request
+     * @return
+     */
+    private String getIp(HttpServletRequest request) {
+        String ip = request.getHeader("X-Real-IP");
+        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("X-Forwarded-For");
+        }
+
+        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+
+        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        return ip;
+    }
+
+    /**
+     * 参数值转成可读的 String
+     *
+     * @param paramValue 待转换的参数值
+     * @return
+     */
+    private String paramValue2String(Object paramValue) {
+
+        if (paramValue == null) {
+            return "";
+        }
+        // String 直接用 reflectionToString 转出来很难看, 这里单独处理一下
+        if (paramValue instanceof String) {
+            return paramValue.toString();
+        }
+        return ToStringBuilder.reflectionToString(paramValue, ToStringStyle.SIMPLE_STYLE);
+    }
+
+    /**
+     * 校验非法的参数
+     *
+     * @param name2Values
+     * @param uuid
+     */
+    private void checkIllegalCharacter(Map<String, Object> name2Values, String uuid) {
+        for (String name : name2Values.keySet()) {
+            Object value = name2Values.get(name);
+            //todo 这里要考虑下是否需要留口,以及参数的设置
+            // 特殊路径,如“文件上传”的请求不做过滤校验处理
+            if ("request".equalsIgnoreCase(name) || "response".equalsIgnoreCase(name)) {
+                continue;
+            }
+
+            // 批量文件 2021-10-23 xumj
+            if (null != value && value.getClass().isArray()) {
+                Object[] obj = (Object[]) value;
+                if (obj.length > 0)
+                    if (obj[0] instanceof MultipartFile)
+                        continue;// 文件类型不处理
+            }
+
+            if (value instanceof MultipartFile) {
+                // 文件类型不处理
+                continue;
+            }
+
+            if (value != null) {
+                String strValue;
+                try {
+                    if (value instanceof String) {
+                        strValue = (String) value;
+                    } else if (value instanceof Long) {
+                        Long longValue = (Long) value;
+                        strValue = String.valueOf(longValue);
+                    } else {
+                        strValue = JSONObject.toJSONString(value);
+                    }
+                } catch (Exception e) {
+                    throw new RuntimeException(uuid);
+                }
+                if (StringUtils.isEmpty(strValue)) {
+                    continue;
+                }
+                validateDataLoop(strValue, uuid, name);
+
+            }
+        }
+    }
+
+    /**
+     * 递归解析提交的JSON格式的条件
+     *
+     * @param jsonStr JSON格式的字符串
+     * @param uuid    事件UUID
+     * @param name    提交的主参数名
+     */
+    private void validateDataLoop(String jsonStr, String uuid, String name) {
+        if (!StringUtils.isEmpty(jsonStr)) {
+            List<String> keywordsList = SensitiveWordsConfig.getFilterKeywords();
+            List<String> charList = SensitiveWordsConfig.getFilterCharacter();
+            //不带空格的关键字
+            List<String> keywordsListNoSpace = SensitiveWordsConfig.getFilterKeywordsNoSpace();
+            if (jsonStr.indexOf("[") == 0) {
+                //jsonArr
+                JSONArray jsonArray = JSONArray.parseArray(jsonStr);
+                for (Object aJsonArray : jsonArray) {
+                    if (aJsonArray instanceof String) {
+                        validateDataLoop((String) aJsonArray, uuid, name);
+                    } else if (aJsonArray instanceof Integer) {
+                        int cheVal = (Integer) aJsonArray;
+                        validateDataLoop(String.valueOf(cheVal), uuid, name);
+                    } else if (aJsonArray instanceof Long) {
+                        long cheVal = (Long) aJsonArray;
+                        validateDataLoop(String.valueOf(cheVal), uuid, name);
+                    } else {
+                        JSONObject jsonObject = (JSONObject) aJsonArray;
+                        for (String s : jsonObject.keySet()) {
+                            String valJson = jsonObject.getString(s);
+                            validateDataLoop(valJson, uuid, s);
+                        }
+                    }
+                }
+            } else if (jsonStr.indexOf("{") == 0) {
+                JSONObject jsonObject = JSONObject.parseObject(jsonStr);
+                for (String key : jsonObject.keySet()) {
+                    //待检验关键字
+                    String chkVal = jsonObject.getString(key);
+                    if (chkVal.indexOf("{") == 0 || chkVal.indexOf("[") == 0) {
+                        validateDataLoop(chkVal, uuid, key);
+                    } else {
+                        checkIllegalFinal(chkVal, keywordsList, charList, keywordsListNoSpace, key, uuid);
+                    }
+                }
+            } else {
+                checkIllegalFinal(jsonStr, keywordsList, charList, keywordsListNoSpace, name, uuid);
+            }
+        }
+    }
+
+    /**
+     * 检查关键字符
+     *
+     * @param chkVal              待检测字符
+     * @param keywordsList        关键字
+     * @param charList            敏感字符
+     * @param keywordsListNoSpace 不带空格的关键字
+     * @param name                提交的主参数名
+     * @param uuid                事件UUID
+     */
+    private void checkIllegalFinal(String chkVal, List<String> keywordsList, List<String> charList, List<String> keywordsListNoSpace, String name, String uuid) {
+        //增加sql语句特例
+        List<String> ignoreKeyList = SensitiveWordsConfig.getIgnoreKey();
+        if (ignoreKeyList.contains(name)) {
+            return;
+        }
+        List<String> splitterValues = Splitter.on(' ').trimResults().omitEmptyStrings().splitToList(chkVal);
+        for (String s : splitterValues) {
+            //不带空格的关键字
+            for (String keyWords : keywordsListNoSpace) {
+                if (s.toLowerCase().contains(keyWords)) {
+                    log.info("Illegal keywords(No Space), name: {}, value: {}, illegal: {}, uuid: {}", name, chkVal, keyWords, uuid);
+                    throw new GlobalException(CodeSecurity.LLEGAL_KEYWORDS, ImmutableMap.of(name, keyWords), null);
+                }
+            }
+            //带空格的关键字
+            for (String keyWords : keywordsList) {
+                if (s.equalsIgnoreCase(keyWords)) {
+                    log.info("Illegal keywords, name: {}, value: {}, illegal: {}, uuid: {}", name, chkVal, keyWords, uuid);
+                    throw new GlobalException(CodeSecurity.LLEGAL_KEYWORDS, ImmutableMap.of(name, keyWords), null);
+                }
+            }
+            //特殊符号检查
+            for (String keyWords : charList) {
+                if (s.equalsIgnoreCase(keyWords)) {
+                    log.info("Illegal keywords, name: {}, value: {}, illegal: {}, uuid: {}", name, chkVal, keyWords, uuid);
+                    throw new GlobalException(CodeSecurity.LLEGAL_KEYWORDS, ImmutableMap.of(name, keyWords), null);
+                }
+            }
+        }
+
+//      for (String chars : charList) {
+//         if (chkVal.toLowerCase().contains(chars)) {
+//            log.info("Illegal character, name: {}, value: {}, illegal: {}, uuid: {}", name, chkVal, chars, uuid);
+//            throw new GlobalException(CodeSecurity.LLEGAL_KEYWORDS, ImmutableMap.of(name, chars), null);
+//         }
+//      }
+    }
+}

+ 79 - 0
src/main/java/com/dtb/portal/config/interceptor/SensitiveWordsConfig.java

@@ -0,0 +1,79 @@
+package com.dtb.portal.config.interceptor;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import lombok.Getter;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 敏感词关键字配置
+ *
+ * @author zengfan
+ */
+@Getter
+public class SensitiveWordsConfig {
+   /**
+    * 待过滤关键词(含空格)
+    */
+   private static List<String> filterKeywords = Lists.newArrayList();
+
+   /**
+    * 待过滤关键词(不带空格)
+    */
+   private static List<String> filterKeywordsNoSpace = Lists.newArrayList();
+
+   /**
+    * 待过滤的字符
+    */
+   private static List<String> filterCharacter = Lists.newArrayList();
+
+   /**
+    * 待过滤的字符(URL)
+    */
+   private static List<String> filterCharacterUrl = Lists.newArrayList();
+
+   /**
+    * 忽略的字符
+    */
+   private static List<String> ignoreKey = Lists.newArrayList();
+
+   static { // 移到配置文件里
+      String keywordStr = "and,exec,count,chr,mid,master,or,truncate,char,declare,join,";
+      filterKeywords.addAll(Splitter.on(',').trimResults().omitEmptyStrings().splitToList(keywordStr).stream().map(String::toLowerCase).collect(Collectors.toList()));
+
+      String keywordStrNoSpace = "insert,select,delete,update,create,drop,";
+      filterKeywordsNoSpace.addAll(Splitter.on(',').trimResults().omitEmptyStrings().splitToList(keywordStrNoSpace).stream().map(String::toLowerCase).collect(Collectors.toList()));
+
+      String characterStr = "<,>,/*,*/,’,|,;,&,$,\",(,),+,0x0d,0x0a,\'";
+      filterCharacter.addAll(Splitter.on(',').trimResults().omitEmptyStrings().splitToList(characterStr).stream().map(String::toLowerCase).collect(Collectors.toList()));
+
+      String characterStrURL = "<,>,/*,*/,|,;,$,\",(,),+,0x0d,0x0a,\'";
+      filterCharacterUrl.addAll(Splitter.on(',').trimResults().omitEmptyStrings().splitToList(characterStrURL).stream().map(String::toLowerCase).collect(Collectors.toList()));
+
+      String ignoreKeyStr = "orderBy,content,whereContent,functionContent,fromContent,auditField,otherAudit";
+      ignoreKey.addAll(Splitter.on(',').trimResults().omitEmptyStrings().splitToList(ignoreKeyStr).stream().collect(Collectors.toList()));
+
+   }
+
+   public static List<String> getFilterKeywords() {
+      return filterKeywords;
+   }
+
+   public static List<String> getFilterCharacter() {
+      return filterCharacter;
+   }
+
+   public static List<String> getFilterKeywordsNoSpace() {
+      return filterKeywordsNoSpace;
+   }
+
+   public static List<String> getFilterCharacterUrl() {
+      return filterCharacterUrl;
+   }
+
+   public static List<String> getIgnoreKey() {
+      return ignoreKey;
+   }
+}

+ 29 - 0
src/main/java/com/dtb/portal/config/interceptor/TokenConfig.java

@@ -0,0 +1,29 @@
+package com.dtb.portal.config.interceptor;
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Getter
+@Component
+public class TokenConfig {
+
+  /* @Value("${dtb.security.header}")
+   private String header;
+
+   @Value("${dtb.security.prefix}")
+   private String prefix;
+
+   @Value("${dtb.security.secret}")
+   private String secret;
+
+   @Value("${dtb.security.expiration}")
+   private long expiration;
+
+   @Value("${dtb.security.redisExpire}")
+   private long redisExpire;
+
+   @Value("${dtb.security.loginUrl}")
+   private String loginUrl;*/
+
+}

+ 32 - 0
src/main/java/com/dtb/portal/config/interceptor/TokenEnum.java

@@ -0,0 +1,32 @@
+
+package com.dtb.portal.config.interceptor;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum TokenEnum {
+
+    TOKEN_USER_ID("id", "token_user_id"),
+
+    TOKEN_DEPARTMENT_ID("departmentId", "token_department_id"),
+
+    TOKEN_NAME("name", "token_name"),
+
+    TOKEN_ACCOUNT("accouny", "token_account"),
+
+    TOKEN_PHONE("phone", "token_phone"),
+
+    TOKEN_EMPLOYEE_ID("employeeId", "token_employee_id"),
+
+    TOKEN_ROLE_ID("roleId","token_role_id"),
+    TOKEN_ROLE_NAME("roleName","token_role_name");
+
+
+
+    private String code;
+
+    private String message;
+
+}

+ 149 - 0
src/main/java/com/dtb/portal/config/interceptor/TokenUtils.java

@@ -0,0 +1,149 @@
+package com.dtb.portal.config.interceptor;
+
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author xumj
+ * @date 2021-10-23
+ */
+@Slf4j
+@Component
+public class TokenUtils {
+
+/*    @Autowired
+    private static TokenConfig config;*/
+
+
+    private static String header="Authorization";
+
+    private static String prefix="Bearer";
+
+    private static  String secret="otherpeopledontknowit";
+
+    private static long expiration=2592000000L;
+
+
+/*
+    public TokenUtils(TokenConfig config) {
+        this.config = config;
+    }
+*/
+
+    public static String getOriginalToken(String token) {
+        String tokenStr = "";
+        if (StringUtils.isNotBlank(token)) {
+            tokenStr = token.substring(prefix.length());
+        }
+        return tokenStr;
+    }
+
+
+    /**
+     * 创建JWT的token
+     */
+    public static String createToken(String userId, String departmentId, String name, String account, String phone) {
+        long nowMillis = System.currentTimeMillis();
+        Map<String, Object> claims = new HashMap<>();
+        claims.put(TokenEnum.TOKEN_USER_ID.getMessage(), userId);
+        claims.put(TokenEnum.TOKEN_DEPARTMENT_ID.getCode(), departmentId);
+        claims.put(TokenEnum.TOKEN_NAME.getMessage(), name);
+        claims.put(TokenEnum.TOKEN_ACCOUNT.getMessage(), account);
+        claims.put(TokenEnum.TOKEN_PHONE.getMessage(), phone);
+        // 创建jwt
+        JwtBuilder builder = Jwts.builder()
+                .setClaims(claims)
+                .setSubject(account)
+                .setIssuedAt(new Date(nowMillis))
+                .setExpiration(generateExpirationDate(nowMillis))
+                 .signWith(SignatureAlgorithm.HS512, secret);
+        return prefix.concat(" ") + builder.compact();
+    }
+
+
+    /**
+     * 生成token的过期时间
+     */
+    private static Date generateExpirationDate(long timeMillis) {
+        return new Date(timeMillis + (Long.valueOf(expiration) * 1000));
+    }
+
+    /**
+     * 判断token是否已经失效
+     *
+     * @return true-失效
+     */
+    public static boolean isTokenExpired(String token) {
+        Date expiredDate = getExpiredDateFromToken(token);
+        return expiredDate == null ? true : expiredDate.before(new Date());
+    }
+
+    /**
+     * 从token中获取过期时间
+     */
+    private static Date getExpiredDateFromToken(String token) {
+        Claims claims = getClaimsFromToken(token);
+        return claims == null ? null : claims.getExpiration();
+    }
+
+    /**
+     * 从token中获取JWT中的负载
+     */
+    public static Claims getClaimsFromToken(String token) {
+        Claims claims = null;
+        try {
+            token = token.replaceAll("Bearer ", "");
+            claims = Jwts.parser()
+                     .setSigningKey(secret)
+                    .parseClaimsJws(token)
+                    .getBody();
+        } catch (Exception e) {
+            log.info("JWT格式验证失败:{}", token);
+        }
+        return claims;
+    }
+
+    /**
+     * 从token中获取用户主键
+     */
+    public static String getUserId() {
+        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = servletRequestAttributes.getRequest();
+        String token = request.getHeader(header);
+        String userId;
+        try {
+            Claims claims = getClaimsFromToken(token);
+            userId = (String) claims.get(TokenEnum.TOKEN_USER_ID.getMessage());
+        } catch (Exception e) {
+            throw new RuntimeException("获取用户主键失败");
+        }
+        return userId;
+    }
+
+    public static void main(String[] args) {
+        String userid = "1";
+
+        String token = createToken(userid, null, null, "18289387963", "18289387963");
+        System.out.println(token);
+
+        Claims claims = getClaimsFromToken(token);
+        String user = (String) claims.get(TokenEnum.TOKEN_USER_ID.getMessage());
+        System.out.println(user);
+    }
+
+}

+ 54 - 0
src/main/java/com/dtb/portal/config/redis/RedisConfig.java

@@ -0,0 +1,54 @@
+package com.dtb.portal.config.redis;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+
+@Component
+public class RedisConfig {
+
+	@Bean
+	public KeyGenerator keyGenerator() {
+		return new KeyGenerator() {
+
+			@Override
+			public Object generate(Object target, Method method, Object... params) {
+				StringBuilder sb = new StringBuilder();
+				sb.append(target.getClass().getName());
+				sb.append(method.getName());
+				for(Object obj : params) {
+					sb.append(obj.toString());
+				}
+				return sb.toString();
+			}
+		};
+	}
+	
+	@Bean
+	public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+		RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
+		template.setConnectionFactory(redisConnectionFactory);
+		template.setKeySerializer(new StringRedisSerializer());
+		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+		ObjectMapper om = new ObjectMapper();
+		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+		jackson2JsonRedisSerializer.setObjectMapper(om);
+		template.setValueSerializer(stringRedisSerializer);
+		template.setHashKeySerializer(stringRedisSerializer);
+		template.setHashValueSerializer(jackson2JsonRedisSerializer);
+		return template;
+	}
+	
+}

+ 72 - 0
src/main/java/com/dtb/portal/config/swagger/SwaggerConfig.java

@@ -0,0 +1,72 @@
+package com.dtb.portal.config.swagger;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.dtb.portal"))
+                .paths(PathSelectors.any())
+                .build()
+                .securitySchemes(securitySchemes())
+                .securityContexts(securityContexts());
+    }
+
+    // 配置在线文档的基本信息
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("PC登录-微信登录系统")
+                .description("PC登录-微信登录系统")
+                .version("1.0").build();
+    }
+
+    private List<SecurityScheme> securitySchemes() {
+        List<SecurityScheme> securitySchemeList = new ArrayList<>();
+        // 配置基于 ApiKey 的鉴权对象
+        // name 为参数名,keyname 是页面传值显示的key的名称,passAs 在swagger鉴权中使用
+        securitySchemeList.add(new ApiKey("Authorization", "Authorization", "header"));
+        return securitySchemeList;
+    }
+
+    private List<SecurityContext> securityContexts() {
+        List<SecurityContext> securityContexts = new ArrayList<>();
+        securityContexts.add(
+                SecurityContext.builder()
+                        .securityReferences(defaultAuth())
+                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
+                        .build());
+        return securityContexts;
+    }
+
+
+    /**
+     * 配置默认的全局鉴权策略;其中返回的 SecurityReference 中,reference 即为ApiKey对象里面的name,保持一致才能开启全局鉴权
+     */
+    private List<SecurityReference> defaultAuth() {
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        List<SecurityReference> securityReferences = new ArrayList<>();
+        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
+        return securityReferences;
+    }
+}

+ 98 - 0
src/main/java/com/dtb/portal/constans/CommonConstant.java

@@ -0,0 +1,98 @@
+package com.dtb.portal.constans;
+
+/**
+ * @author fuzhiyou
+ * @date 2022-03-22 10:23
+ */
+
+
+/**
+ * 定义一些静态的默认属性
+ */
+public interface CommonConstant {
+
+    /**
+     * user in  portal
+     */
+    public final static String USER_TYPE_1 = "1";
+    /**
+     * default password is 123456
+     */
+    public final static String DEFAULT_PASSWORD = "123456";
+
+
+    interface App {
+        /**
+         * app认证地址
+         */
+        public final static String APP_IDENTIFIER = "192.168.110.114";
+
+        /**
+         * app默认是id_token,暂时先不用jwks
+         */
+        public final static String APP_JWKS = "id_token";
+
+
+        /**
+         * app  protocol
+         */
+        public final static String APP_PROTOCOL = "Oauth2";
+    }
+
+    /**
+     * default role type id id=1
+     */
+    public final static String DEFAULT_ROLE_ID = "1";
+
+    /**
+     * default status is 0
+     */
+    public final static char DEFAULT_STATUS = '0';
+
+    /**
+     * default Value is 0
+     */
+    public final static String DEFAULT_0 = "0";
+
+
+    /**
+     * DEFAULT_AUTH_PATH_PREFIX
+     */
+    public final static String DEFAULT_AUTH_PATH_PREFIX = "http://192.168.110.114:9007/S-DaaS-auth/userpool/";
+
+
+    /**
+     * file type
+     */
+
+    interface FileType {
+        public final static String FILE_TYPE_APP = "0";
+        public final static String FILE_TYPE_USER_POOL = "1";
+        public final static String FILE_TYPE_USER = "2";
+    }
+
+
+    /**
+     * file path prefix
+     */
+    public final static String FILE_PATH_PREFIX = "http://192.168.110.114:9007";
+
+
+    interface UserAddType {
+
+        /**
+         * use username add
+         */
+        public final static String USER_ADD_TYPE_0 = "0";
+
+        /**
+         * use phone add
+         */
+        public final static String USER_ADD_TYPE_1 = "1";
+
+        /**
+         * use email add
+         */
+        public final static String USER_ADD_TYPE_2 = "2";
+    }
+}

+ 68 - 0
src/main/java/com/dtb/portal/constans/TokenConstants.java

@@ -0,0 +1,68 @@
+package com.dtb.portal.constans;
+
+
+import com.dtb.portal.util.PrimaryIdUtils;
+
+/**
+ * @author 福大吊毛
+ * @date 2022-01-05 17:10
+ */
+public interface TokenConstants {
+
+
+    String JTI = PrimaryIdUtils.getId().toString();
+    /**
+     * 令牌的发布者
+     */
+    String ISS = "S-DaaS";
+    /*    *//**
+     * 令牌的ID
+     *//*
+    String JTI = PrimaryIdUtils.getId().toString();*/
+
+    /**
+     * 账号的权限类型的前缀
+     */
+    String AUTHORITIES = "authorities";
+
+
+    /**
+     * 生成token使用的secret
+     */
+
+    String SECRET = "qwertyuiopasdfghjklmnbvcxz!@#$%^1234567890";
+
+    /**
+     * token 存到请求请求头里的名称
+     */
+    String HEADER = "Authorization";
+
+    /**
+     * token 存到请求请求头里值的前缀
+     */
+    String PREFIX = "Bearer";
+    /**
+     * token存到cookie中的名称
+     */
+    String SDAAS_TOKEN = "sdaas-token";
+
+    /**
+     * 当前登录用户前缀
+     */
+    String CURRENT_LOGIN_USER = "current_login_user:";
+
+    String USER_ID = "user_id";
+
+    /**
+     * 用户手机号
+     */
+    String USER_PHONE = "user_phone";
+
+
+    /**
+     * 用户邮箱
+     */
+    String USER_EMAIL = "user_email";
+
+
+}

+ 43 - 0
src/main/java/com/dtb/portal/constans/WxAppApiConstant.java

@@ -0,0 +1,43 @@
+package com.dtb.portal.constans;
+
+public class WxAppApiConstant {
+
+    //https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
+    //public static final String CODE_TO_SESSION = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
+    public static final String CODE_TO_SESSION = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code";
+
+    public static final String GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
+
+    public static final String GET_UNLIMITED = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN";
+
+    public static final String SEND_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN";
+
+    public static final String SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
+
+    public static final String WX_APP_TOKEN = "wx-app-token";
+
+    public static final String OPEN_ID = "openid";
+
+    // 获取手机号接口
+    public static final String GET_PHONE_NUMBER = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN";
+
+    //模板
+    public static final String APPOINTMENT_SUCCESS_TEMPLATE_ID = "ltJirk81WHNZZA60coMm021QRd5yyLdLc1ZnJtP-788";
+
+    public static final String ARCHIVE_NOTICE_TEMPLATE_ID = "lDAS64Jd1B6qDKY_cDEr3D5qIFPEh8a-iSVl1BsIEZI";
+
+    public static final String FOLLOW_COMPLETE_NOTICE_TEMPLATE_ID = "x5N-Z_thuHn_CBtgDjJm8NiPvf7pwb9ouIEnGVtoWMw";
+
+    public static final String FOLLOW_NEAR_COMPLETE_NOTICE_TEMPLATE_ID = "D6aIJsQcy66Qus-avXlCeqQQz5QBghjcE58rbgpRAFc";
+
+    public static final String FOLLOW_BEGIN_NOTICE_TEMPLATE_ID = "SpOFQQr4yAB71PyykI2iRx1MAhh_DD9BvI2aNcDcE0g";
+
+    public static final String APPOINTMENT_CHANGE_NOTICE_TEMPLATE_ID = "rxRzj7EwMYxm22EjGmqLSrxCBxXrd5MbPpDbUF77KR0";
+
+    public static final String FILL_SURVEY_NOTICE_TEMPLATE_ID = "tk5PBd2N17NNRkVv5U8K5GJNKp0r9ERAwI-dUALCz4M";
+
+    public static final String APPOINTMENT_EXPIRE_NOTICE_TEMPLATE_ID = "tk5PBd2N17NNRkVv5U8K5GJNKp0r9ERAwI-dUALCz4M";
+
+    //页面地址
+    public static final String HOME = "index";
+}

+ 60 - 0
src/main/java/com/dtb/portal/controller/FileController.java

@@ -0,0 +1,60 @@
+package com.dtb.portal.controller;
+
+
+import com.dtb.portal.controller.view.RestResponse;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.util.Objects;
+import java.util.UUID;
+
+@Slf4j
+@RestController
+@Api(tags = {"上传服务器"}, value = "FileController")
+@RequestMapping("/file")
+public class FileController {
+
+    @Value("${file.url}")
+    private String fileUrl;
+
+
+    @ApiOperation("上传头像")
+    @PostMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ";")
+    public RestResponse<String> upload(@RequestParam MultipartFile file) {
+        String completePath = "";
+        try {
+            if (file != null && StringUtils.isNotEmpty(file.getOriginalFilename())) {
+                //获取文件名称
+                String suffixName = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf("."));
+                String fileName = UUID.randomUUID().toString().replaceAll("-", "") + suffixName;
+
+                String filePath ="/www/wwwroot/fun9527.com/logo";
+                File saveFile = new File(filePath, fileName);
+                if (!saveFile.getParentFile().exists()) {
+                    saveFile.getParentFile().mkdirs();
+                }
+                
+                file.transferTo(saveFile);
+                completePath = fileUrl + "/logo/" + fileName;
+            }
+        } catch (Exception e) {
+            log.error("上传文件错误:", e);
+            throw new RuntimeException("上传文件错误");
+        }
+
+        log.info("返回的路径是:{}", completePath);
+        return RestResponse.ok(completePath);
+
+    }
+
+}

+ 220 - 0
src/main/java/com/dtb/portal/controller/LoginController.java

@@ -0,0 +1,220 @@
+package com.dtb.portal.controller;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.dtb.portal.config.interceptor.LoginClassAnnotation;
+import com.dtb.portal.controller.view.RestResponse;
+import com.dtb.portal.entity.SysUser;
+import com.dtb.portal.service.impl.SysUserServiceImpl;
+import com.dtb.portal.util.WxPhoneNumberUtils;
+
+import io.swagger.annotations.*;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@RestController
+@Api(tags = {"登录控制器"}, value = "LoginController")
+@LoginClassAnnotation
+public class LoginController {
+
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    @Autowired
+    private SysUserServiceImpl userService;
+
+
+
+
+    @ApiOperation("登录")
+    @GetMapping("/login")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "accountName", required = true, value = "用户名"),
+            @ApiImplicitParam(name = "password", required = true, value = "密码"),
+    })
+    public RestResponse<SysUser> login(@RequestParam(name = "accountName", required = true) String accountName,
+                                       @RequestParam(name = "password", required = false) String password, HttpServletRequest request) {
+
+        SysUser sysUser = userService.login(accountName, password);
+        return RestResponse.ok(sysUser);
+    }
+
+    @ApiOperation("登出")
+    @GetMapping("/logout")
+    public RestResponse<?> logout(HttpServletRequest request) {
+        return RestResponse.ok();
+    }
+
+  /*  @ApiOperation("生成验证码")
+    @GetMapping("generateCode")
+    RestResponse<Map<String, Object>> getCodeImage() {
+        return RestResponse.ok(getRandomCodeImage());
+    }*/
+
+    @ApiOperation(value = "APP微信登录")
+    @RequestMapping(value = "loginByWxApp", method = RequestMethod.GET)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "code", required = true, value = "微信登录code"),
+            @ApiImplicitParam(name = "avatarUrl", required = false, value = "头像URL"),
+            @ApiImplicitParam(name = "nickName", required = false, value = "昵称")
+    })
+    public RestResponse<SysUser> loginByWxApp(
+            @RequestParam(value = "code", required = true) String code,
+            @RequestParam(value = "avatarUrl", required = false) String avatarUrl,
+            @RequestParam(value = "nickName", required = false) String nickName) {
+        log.info("收到APP微信登录请求 - code: {}, nickName: {}", code, nickName);
+        try {
+            SysUser sysUser = userService.loginByWxApp(code, avatarUrl, nickName);
+            log.info("APP微信登录成功 - userId: {}, openId: {}", sysUser.getId(), sysUser.getOpenId());
+            return RestResponse.ok(sysUser);
+        } catch (Exception e) {
+            log.error("APP微信登录失败 - code: {}, error: {}", code, e.getMessage());
+            throw e;
+        }
+    }
+
+    @ApiOperation(value = "小程序微信登录")
+    @RequestMapping(value = "loginByWxMini", method = RequestMethod.GET)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "code", required = true, value = "微信登录code"),
+            @ApiImplicitParam(name = "phoneCode", required = true, value = "手机号code")
+    })
+    public RestResponse<SysUser> loginByWxMini(
+            @RequestParam(value = "code", required = true) String code,
+            @RequestParam(value = "phoneCode", required = true) String phoneCode) {
+        log.info("收到小程序微信登录请求 - code: {}, phoneCode: {}", code, phoneCode);
+        try {
+            SysUser sysUser = userService.loginByWxMini(code, phoneCode);
+            log.info("小程序微信登录成功 - userId: {}, openId: {}", sysUser.getId(), sysUser.getOpenId());
+            return RestResponse.ok(sysUser);
+        } catch (Exception e) {
+            log.error("小程序微信登录失败 - code: {}, phoneCode: {}, error: {}", code, phoneCode, e.getMessage());
+            throw e;
+        }
+    }
+
+    @ApiOperation(value = "发送短信验证码")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phone", required = true, value = "手机号")
+    })
+    @RequestMapping(value = "sendSms", method = RequestMethod.GET)
+    public RestResponse<Object> sendSms(@RequestParam(value = "phone") String phone) {
+        userService.sendSms(phone);
+        return RestResponse.ok();
+    }
+
+    @ApiOperation(value = "短信验证码登录")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phone", required = true, value = "手机号"),
+            @ApiImplicitParam(name = "checkCode", required = true, value = "验证码"),
+            @ApiImplicitParam(name = "openId", required = false, value = "微信openId")
+    })
+    @RequestMapping(value = "loginBySms", method = RequestMethod.GET)
+    public RestResponse<SysUser> loginBySms(@RequestParam(value = "phone") String phone,
+                                            @RequestParam(value = "checkCode") String checkCode,
+                                            @RequestParam(value = "openId", required = false) String openId) {
+        SysUser sysUser = userService.loginBySms(phone, checkCode, openId);
+        // newUser 字段已在 service 层设置
+        return RestResponse.ok(sysUser);
+    }
+
+
+    @ApiOperation(value = "绑定手机号")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", required = true, value = "用户id"),
+            @ApiImplicitParam(name = "phone", required = true, value = "手机号"),
+            @ApiImplicitParam(name = "checkCode", required = true, value = "验证码")
+    })
+    @RequestMapping(value = "bindPhone", method = RequestMethod.GET)
+    public RestResponse<SysUser> bindPhone(
+            @RequestParam(value = "id") String id,
+            @RequestParam(value = "phone") String phone,
+            @RequestParam(value = "checkCode") String checkCode) {
+        SysUser sysUser = userService.bindPhone(id, phone, checkCode);
+        return RestResponse.ok(sysUser);
+    }
+
+    @RequestMapping(value = "forgotPasswordByPhone", method = RequestMethod.GET)
+    public RestResponse<SysUser> forgotPasswordByPhone(
+            @RequestParam(value = "phone") String phone,
+            @RequestParam(value = "password") String password,
+            @RequestParam(value = "checkCode") String checkCode) {
+        SysUser sysUser = userService.forgotPasswordByPhone(phone,password, checkCode);
+        return RestResponse.ok(sysUser);
+    }
+
+    @RequestMapping(value = "forgotPassword", method = RequestMethod.GET)
+    public RestResponse<SysUser> forgotPassword(
+            @RequestParam(value = "phone") String phone) {
+        SysUser sysUser = userService.forgotPassword(phone);
+        return RestResponse.ok(sysUser);
+    }
+
+    @ApiOperation(value = "微信手机号登录")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phoneCode", required = true, value = "微信获取手机号的code"),
+            @ApiImplicitParam(name = "loginCode", required = true, value = "微信登录获取openId的code")
+    })
+    @RequestMapping(value = "loginByWxPhoneNumber", method = RequestMethod.GET)
+    public RestResponse<SysUser> loginByWxPhoneNumber(
+            @RequestParam(value = "phoneCode", required = true) String phoneCode,
+            @RequestParam(value = "loginCode", required = true) String loginCode) {
+        log.info("收到微信手机号登录请求 - phoneCode: {}, loginCode: {}", phoneCode, loginCode);
+        try {
+            SysUser sysUser = userService.loginByWxPhoneNumber(phoneCode, loginCode);
+            log.info("微信手机号登录成功 - userId: {}, phone: {}", sysUser.getId(), sysUser.getPhone());
+            return RestResponse.ok(sysUser);
+        } catch (Exception e) {
+            log.error("微信手机号登录失败 - phoneCode: {}, loginCode: {}, 错误: {}", phoneCode, loginCode, e.getMessage());
+            throw e;
+        }
+    }
+
+    @ApiOperation(value = "调试微信手机号code")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "code", required = true, value = "微信获取手机号的code")
+    })
+    @RequestMapping(value = "debugWxPhoneCode", method = RequestMethod.GET)
+    public RestResponse<Object> debugWxPhoneCode(
+            @RequestParam(value = "code", required = true) String code) {
+        String appId = userService.getWxAppId();
+        String appSecret = userService.getWxAppSecret();
+        JSONObject debugInfo = WxPhoneNumberUtils.debugCode(appId, appSecret, code);
+        return RestResponse.ok(debugInfo);
+    }
+
+    @ApiOperation(value = "调试APP微信配置")
+    @RequestMapping(value = "debugWxAppConfig", method = RequestMethod.GET)
+    public RestResponse<Object> debugWxAppConfig() {
+        JSONObject debug = new JSONObject();
+        try {
+            String appId = userService.getWxAppAppId();
+            String appSecret = userService.getWxAppAppSecret();
+            
+            debug.put("appId", appId);
+            debug.put("appSecret", "***" + appSecret.substring(appSecret.length() - 4)); // 只显示最后4位
+            
+            // 测试获取access_token
+            String url = String.format(
+                "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
+                appId, appSecret);
+            
+            JSONObject result = WxPhoneNumberUtils.httpGet(url);
+            debug.put("test_result", result);
+            
+            return RestResponse.ok(debug);
+        } catch (Exception e) {
+            debug.put("error", e.getMessage());
+            return RestResponse.ok(debug);
+        }
+    }
+
+}

+ 25 - 0
src/main/java/com/dtb/portal/controller/TestController.java

@@ -0,0 +1,25 @@
+package com.dtb.portal.controller;
+
+import com.dtb.portal.config.AliyunConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 测试控制器
+ */
+@RestController
+@RequestMapping("/test")
+public class TestController {
+    
+    @Autowired
+    private AliyunConfig aliyunConfig;
+    
+    @GetMapping("/signName")
+    public String testSignName() {
+        aliyunConfig.printSignName();
+        return "原始signName: " + aliyunConfig.getSignName() + 
+               ", 处理后的signName: " + aliyunConfig.getSignName();
+    }
+} 

+ 148 - 0
src/main/java/com/dtb/portal/controller/UserController.java

@@ -0,0 +1,148 @@
+package com.dtb.portal.controller;
+
+
+import com.dtb.portal.controller.view.RestResponse;
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.controller.view.request.UserAddByNamePassRequest;
+import com.dtb.portal.controller.view.request.UserAddRequest;
+import com.dtb.portal.controller.view.request.UserRemoveNotCheckRequest;
+import com.dtb.portal.controller.view.request.UserRemoveRequest;
+import com.dtb.portal.controller.view.request.UserRequest;
+import com.dtb.portal.controller.view.request.UserUpdateRequest;
+import com.dtb.portal.entity.SysUser;
+import com.dtb.portal.service.impl.SysUserServiceImpl;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+
+
+@Slf4j
+@RestController
+@Api(tags = {"用户管理"}, value = "UserController")
+@RequestMapping("/user")
+public class UserController {
+
+    @Autowired
+    private SysUserServiceImpl userService;
+
+
+    @ApiOperation("用户分页")
+    @PostMapping("/page")
+    public RestResponse<PageInfo<SysUser>> page(@RequestBody PageQuery<UserRequest> pageQuery) {
+        PageInfo<SysUser> pageInfo = userService.page(pageQuery);
+        return RestResponse.ok(pageInfo);
+    }
+
+    @ApiOperation("用户禁用")
+    @GetMapping("/enable")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", required = true, value = "用户主键"),
+            @ApiImplicitParam(name = "enable", required = true, value = "是否禁用", example = "true:是 false:否"),
+    })
+    public RestResponse<Object> enable(@RequestParam(value = "id", required = true) String id,
+                                       @RequestParam(value = "enable", required = true) String enable) {
+
+        userService.enable(id, enable);
+        return RestResponse.ok();
+    }
+
+    @ApiOperation("修改密码")
+    @GetMapping("/updatePassword")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", required = true, value = "用户主键"),
+            @ApiImplicitParam(name = "password", required = true, value = "用户密码"),
+    })
+    public RestResponse<Object> updatePassword(@RequestParam(value = "id", required = true) String id,
+                                               @RequestParam(value = "password", required = true) String password) {
+        userService.updatePassword(id, password);
+        return RestResponse.ok();
+    }
+
+    @ApiOperation("根据openID查询用户")
+    @GetMapping("/findByOpenId")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "openId", required = true, value = "微信小程序的openid值"),
+    })
+    public RestResponse<SysUser> findByOpenId(@RequestParam(value = "openId", required = true) String openId) {
+        SysUser sysUser = userService.findByOpenId(openId);
+        return RestResponse.ok(sysUser);
+    }
+
+    @ApiOperation("修改用户-微信端")
+    @PostMapping("/update")
+    public RestResponse<SysUser> update(@RequestBody UserUpdateRequest request) {
+        SysUser sysUser = userService.update(request);
+        return RestResponse.ok(sysUser);
+    }
+
+    @ApiOperation("添加用户-微信端")
+    @PostMapping("/add")
+    public RestResponse<SysUser> add(@RequestBody UserAddRequest request) {
+        SysUser sysUser = userService.add(request);
+        return RestResponse.ok(sysUser);
+    }
+    @ApiOperation("添加用户")
+    @PostMapping("/addByNamePass")
+    public RestResponse<SysUser> addByNamePass(@RequestBody UserAddByNamePassRequest request) {
+        SysUser sysUser = userService.addByNamePass(request);
+        return RestResponse.ok(sysUser);
+    }
+    @ApiOperation("注销用户")
+    @PostMapping("/delete")
+    public RestResponse<SysUser> delete(@RequestBody UserRemoveRequest request) {
+        SysUser sysUser =  userService.delete(request);
+        return RestResponse.ok(sysUser);
+    }
+    @ApiOperation("注销用户不需要验证码直接删除")
+    @PostMapping("/deleteNotCheck")
+    public RestResponse<SysUser> deleteNotCheck(@RequestBody UserRemoveNotCheckRequest request) {
+        SysUser sysUser =  userService.deleteNotCheck(request);
+        return RestResponse.ok(sysUser);
+    }
+    @ApiOperation("获取用户详细")
+    @GetMapping("/get")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", required = true, value = "用户主键"),
+    })
+    public RestResponse<SysUser> get(@RequestParam(value = "id", required = true) String id) {
+        SysUser sysUser = userService.get(id);
+        return RestResponse.ok(sysUser);
+    }
+
+    @ApiOperation(value = "更换微信openId")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", required = true, value = "用户id"),
+            @ApiImplicitParam(name = "newOpenId", required = true, value = "新的微信openId")
+    })
+    @PostMapping("/changeOpenId")
+    public RestResponse<SysUser> changeOpenId(@RequestBody ChangeOpenIdRequest req) {
+        SysUser sysUser = userService.changeOpenId(req.id, req.newOpenId);
+        return RestResponse.ok(sysUser);
+    }
+
+    public static class ChangeOpenIdRequest {
+        public String id;
+        public String newOpenId;
+    }
+
+    @ApiOperation("欢迎")
+    @GetMapping("/hello")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", required = true, value = "用户主键"),
+    })
+    public RestResponse<Object> hello(@RequestParam(value = "id", required = true) String id) {
+        HashMap<String, Object> hashMap = new HashMap<>();
+
+        hashMap.put("code",200);
+        hashMap.put("msg","欢迎光临"+id);
+
+        return RestResponse.ok(hashMap);
+    }
+}

+ 222 - 0
src/main/java/com/dtb/portal/controller/VideoController.java

@@ -0,0 +1,222 @@
+package com.dtb.portal.controller;
+
+
+import com.dtb.portal.config.interceptor.RedisUtils;
+import com.dtb.portal.controller.view.RestResponse;
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.controller.view.request.UserRequest;
+import com.dtb.portal.controller.view.request.VideoRequest;
+import com.dtb.portal.dto.VideoUploadDTO;
+import com.dtb.portal.entity.SysUser;
+import com.dtb.portal.entity.Video;
+import com.dtb.portal.mapper.SysUserMapper;
+import com.dtb.portal.mapper.VideoMapper;
+import com.dtb.portal.service.impl.SysUserServiceImpl;
+import com.dtb.portal.service.impl.VideoServiceImpl;
+import com.dtb.portal.util.AliyunOSSUtil;
+import com.dtb.portal.util.PrimaryIdUtils;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.util.Date;
+import java.util.Objects;
+import java.util.UUID;
+
+@Slf4j
+@RestController
+@Api(tags = {"OSS文件上传"}, value = "VideoController")
+@RequestMapping("/file")
+public class VideoController {
+
+    @Value("${file.url}")
+    private String fileUrl;
+
+    @Autowired
+    private AliyunOSSUtil aliyunOSSUtil;
+
+    @Autowired
+    private VideoMapper videoMapper;
+    @Autowired
+    private RedisUtils redisUtils;
+
+    @Autowired
+    private VideoServiceImpl videoService;
+    private static String header = "Authorization";
+
+
+    @ApiOperation("上传视频")
+    @PostMapping(value = "upload/video", consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ";")
+    public RestResponse<String> upload(@RequestParam("file") MultipartFile file,
+                                       @RequestParam(value = "fileKey", required = true) String fileKey) {
+        String completePath = "";
+        String fileType ="";
+        // 1. 校验文件是否为空
+        if (file == null || file.isEmpty()) {
+            return RestResponse.exception("上传文件不能为空");
+        }
+        String originalFilename = file.getOriginalFilename();
+        if (originalFilename == null || !originalFilename.matches(".*\\.(mp4|avi|mov|wmv|flv|mkv)$")) {
+            return RestResponse.exception("仅支持视频文件格式:mp4, avi, mov, wmv, flv, mkv");
+        }
+        fileType = "video";
+
+        //校验文件大小(如限制500MB)
+        long maxSize = 500 * 1024 * 1024L;
+        if (file.getSize() > maxSize) {
+            return RestResponse.exception("文件大小不能超过500MB");
+        }
+        String filePath = "/www/wwwroot/fun9527.com";
+        String totalPath = "";
+        File saveFile =null;
+        try {
+            if (file != null && StringUtils.isNotEmpty(originalFilename)) {
+                //获取文件名称
+                String suffixName = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf("."));
+                String fileName = file.getName() + suffixName;
+                //设置文件logo存储的路径
+                totalPath = filePath + "/" + fileName;
+                saveFile = new File(filePath, fileName);
+                if (!saveFile.getParentFile().exists()) {
+                    saveFile.getParentFile().mkdirs();
+                }
+                file.transferTo(saveFile);
+                String uploadUrl = null;
+                if (!"".equals(originalFilename.trim())) {
+                    uploadUrl = aliyunOSSUtil.upload(saveFile, fileType,fileKey);
+                    log.info("uploadUrl=" + uploadUrl);
+                }
+                completePath = "https://susuan-bucket.oss-cn-beijing.aliyuncs.com/" + uploadUrl;
+            }
+        } catch (Exception e) {
+            log.error("上传文件错误:", e);
+            throw new RuntimeException("上传文件错误");
+        } finally {
+            if (!totalPath.equals("")) {
+                deleteFile(totalPath);
+            }
+            saveFile.delete();
+        }
+
+        log.info("返回的路径是:{}", completePath);
+        return RestResponse.ok(completePath);
+
+    }
+
+    @ApiOperation("上传图片")
+    @PostMapping(value = "upload/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ";")
+    public RestResponse<String> uploadImage(@RequestParam("file") MultipartFile file,
+                                            @RequestParam(value = "fileKey", required = true) String fileKey) {
+        String completePath = "";
+        String fileType ="";
+        // 1. 校验文件是否为空
+        if (file == null || file.isEmpty()) {
+            return RestResponse.exception("上传文件不能为空");
+        }
+        // 2. 校验文件类型(后缀判断,实际可用 contentType 更安全)
+        String originalFilename = file.getOriginalFilename();
+        if (originalFilename == null || !originalFilename.matches(".*\\.(jpg|jpeg|png|gif|bmp|webp)$")) {
+            return RestResponse.exception("仅支持图片格式:jpg, jpeg, png, gif, bmp, webp");
+        }
+        // 3. 校验文件大小(如限制5MB)
+        long maxSize = 5 * 1024 * 1024L;
+        if (file.getSize() > maxSize) {
+            return RestResponse.exception("文件大小不能超过5MB");
+        }
+
+        fileType = "image";
+        String filePath = "/www/wwwroot/fun9527.com";
+        String totalPath = "";
+        File saveFile =null;
+        try {
+            if (file != null && StringUtils.isNotEmpty(originalFilename)) {
+                //获取文件名称
+                String suffixName = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf("."));
+                String fileName = file.getName() + suffixName;
+                //设置文件logo存储的路径
+                saveFile = new File(filePath, fileName);
+                if (!saveFile.getParentFile().exists()) {
+                    saveFile.getParentFile().mkdirs();
+                }
+                file.transferTo(saveFile);
+                String uploadUrl = null;
+                if (!"".equals(originalFilename.trim())) {
+                    uploadUrl = aliyunOSSUtil.upload(saveFile, fileType,fileKey);
+                    log.info("uploadUrl=" + uploadUrl);
+                }
+                completePath = "https://susuan-bucket.oss-cn-beijing.aliyuncs.com/" + uploadUrl;
+            }
+        } catch (Exception e) {
+            log.error("上传文件错误:", e);
+            throw new RuntimeException("上传文件错误");
+        }  finally {
+            if (!totalPath.equals("")) {
+                deleteFile(totalPath);
+            }
+            saveFile.delete();
+        }
+
+        log.info("返回的路径是:{}", completePath);
+        return RestResponse.ok(completePath);
+
+    }
+
+    @ApiOperation("视频信息分页")
+    @PostMapping("/page")
+    public RestResponse<PageInfo<Video>> page(@RequestBody PageQuery<VideoRequest> pageQuery) {
+        PageInfo<Video> pageInfo = videoService.page(pageQuery);
+        return RestResponse.ok(pageInfo);
+    }
+
+    @ApiOperation("视频信息修改")
+    @PostMapping("/modify")
+    public RestResponse<PageInfo<Video>> page(@RequestParam Video video) {
+        videoService.update(video);
+        return RestResponse.ok();
+    }
+
+    @ApiOperation("视频信息删除")
+    @PostMapping("/delete")
+    public RestResponse<PageInfo<Video>> page(@RequestParam String id) {
+        videoService.delete(id);
+        return RestResponse.ok();
+    }
+
+    public static  void deleteFile(String storePath){
+        File fileOrDire = new File(storePath);
+        if (fileOrDire.exists()){
+            if (fileOrDire.isDirectory()){
+                //是目录路径 删除全部文件
+                File[] files = fileOrDire.listFiles();
+                for (File file : files){
+                    file.delete();
+                }
+            }
+            //是文件
+            fileOrDire.delete();
+        }
+    }
+
+    @ApiOperation("文件上传进进度查询")
+    @PostMapping("/ratio/query")
+    public RestResponse<String> ratioQuery(@RequestParam String fileKey) {
+        if(RedisUtils.hasKey(fileKey)){
+            return RestResponse.ok(RedisUtils.get(fileKey).toString());
+        }else{
+            return RestResponse.ok("未查询到上传进度");
+        }
+    }
+
+}

+ 66 - 0
src/main/java/com/dtb/portal/controller/VideoUserController.java

@@ -0,0 +1,66 @@
+package com.dtb.portal.controller;
+
+import com.dtb.portal.controller.view.RestResponse;
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.controller.view.request.VideoUserRequest;
+import com.dtb.portal.controller.view.request.VideoUserUpdateRequest;
+import com.dtb.portal.entity.VideoUser;
+import com.dtb.portal.service.VideoUserService;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/video")
+@Api(tags = "视频管理")
+public class VideoUserController {
+    @Autowired
+    private VideoUserService videoUserService;
+
+    @ApiOperation("添加视频")
+    @PostMapping("/add")
+    public RestResponse<VideoUser> add(@RequestBody VideoUserRequest request) {
+        VideoUser data = videoUserService.add(request);
+        return RestResponse.ok(data);
+    }
+
+    @ApiOperation("修改视频")
+    @PostMapping("/update")
+    public RestResponse<VideoUser> update(@RequestBody VideoUserUpdateRequest request) {
+        VideoUser data = videoUserService.update(request);
+        return RestResponse.ok(data);
+    }
+
+    @ApiOperation("删除视频")
+    @GetMapping("/delete")
+    public RestResponse<?> delete(@RequestParam String id) {
+        videoUserService.delete(id);
+        return RestResponse.ok();
+    }
+
+    @ApiOperation("视频分页")
+    @PostMapping("/page")
+    public RestResponse<PageInfo<VideoUser>> page(@RequestBody PageQuery<VideoUserRequest> pageQuery) {
+        PageInfo<VideoUser> pageInfo = videoUserService.page(pageQuery);
+        return RestResponse.ok(pageInfo);
+    }
+
+    @ApiOperation("获取视频详情")
+    @GetMapping("/get")
+    public RestResponse<VideoUser> get(@RequestParam String id) {
+        VideoUser data = videoUserService.get(id);
+        return RestResponse.ok(data);
+    }
+
+    @ApiOperation("视频列表(不分页)")
+    @GetMapping("/list")
+    public RestResponse<List<VideoUser>> list(@RequestParam(required = false) String name,
+                                             @RequestParam(required = false) String content) {
+        List<VideoUser> list = videoUserService.list(name, content);
+        return RestResponse.ok(list);
+    }
+} 

+ 54 - 0
src/main/java/com/dtb/portal/controller/VipGoodsController.java

@@ -0,0 +1,54 @@
+package com.dtb.portal.controller;
+
+import com.dtb.portal.controller.view.RestResponse;
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.controller.view.request.UserRequest;
+import com.dtb.portal.controller.view.request.VipGoodsRequest;
+import com.dtb.portal.entity.VipGoodsData;
+import com.dtb.portal.service.VipGoodsService;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@Api(tags = "VIP商品接口管理")
+@RestController
+@RequestMapping("/vip")
+public class VipGoodsController {
+
+
+    @Autowired
+    private VipGoodsService vipGoodsService;
+
+    @ApiOperation("添加VIP商品")
+    @PostMapping("/add")
+    public RestResponse<VipGoodsData> add(@RequestBody VipGoodsRequest request) {
+        VipGoodsData data = vipGoodsService.add(request);
+        return RestResponse.ok(data);
+    }
+
+    @ApiOperation("修改VIP商品")
+    @PostMapping("/update")
+    public RestResponse<VipGoodsData> update(@RequestBody VipGoodsRequest request) {
+        VipGoodsData data = vipGoodsService.update(request);
+        return RestResponse.ok(data);
+    }
+
+    @ApiOperation("删除商品")
+    @GetMapping("/delete")
+    public RestResponse<VipGoodsData> delete(@RequestParam(name = "id", required = true) String id) {
+        VipGoodsData data = vipGoodsService.delete(id);
+        return RestResponse.ok(data);
+    }
+
+    @ApiOperation("商品分页")
+    @PostMapping("/page")
+    public RestResponse<PageInfo<VipGoodsData>> page(@RequestBody PageQuery<VipGoodsRequest> pageQuery) {
+        PageInfo<VipGoodsData> pageInfo = vipGoodsService.page(pageQuery);
+        return RestResponse.ok(pageInfo);
+    }
+
+}

+ 260 - 0
src/main/java/com/dtb/portal/controller/WxPayController.java

@@ -0,0 +1,260 @@
+package com.dtb.portal.controller;
+
+import com.dtb.portal.controller.view.RestResponse;
+import com.dtb.portal.controller.view.response.ResultMap;
+import com.dtb.portal.entity.AppPayData;
+import com.dtb.portal.entity.SysUser;
+import com.dtb.portal.service.WxPayService;
+import com.dtb.portal.util.WxAppApiUtils;
+import com.wechat.pay.java.service.payments.model.Transaction;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+@Slf4j
+@Api(tags = "微信支付接口管理")
+@RestController
+@RequestMapping("/wxPay")
+public class WxPayController {
+
+    @Autowired
+    private WxPayService wxPayService;
+    
+    @Value("${wechat.miniprogram.app_id}")
+    private String miniprogramAppId;
+    
+    @Value("${wechat.miniprogram.app_secret}")
+    private String miniprogramAppSecret;
+
+    /**
+     * 统一下单接口
+     */
+    @ApiOperation(value = "统一下单-V2", notes = "统一下单")
+    @GetMapping("/unifiedOrder")
+    public ResultMap unifiedOrder(
+            @ApiParam(value = "VIP商品主键") @RequestParam String vipGoodsId,
+            HttpServletRequest request) {
+        try {
+            // 1、验证订单是否存在
+            // 2、开始微信支付统一下单
+            ResultMap resultMap = wxPayService.unifiedOrder(vipGoodsId);
+            return resultMap;//系统通用的返回结果集,见文章末尾
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            return ResultMap.error("运行异常,请联系管理员");
+        }
+    }
+
+    /* */
+
+    /**
+     * 微信支付异步通知
+     *//*
+    @ApiOperation(value = "微信支付异步通知")
+    @GetMapping(value = "/notify")
+    public String payNotify(HttpServletRequest request) {
+        InputStream is = null;
+        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
+        try {
+            is = request.getInputStream();
+            // 将InputStream转换成String
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+            StringBuilder sb = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line + "\n");
+            }
+            xmlBack = wxPayService.notify(sb.toString());
+        } catch (Exception e) {
+            log.error("微信手机支付回调通知失败:", e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return xmlBack;
+    }
+
+    @ApiOperation(value = "退款", notes = "退款")
+    @GetMapping("/refund")
+    public ResultMap refund(@ApiParam(value = "订单号") @RequestParam String orderNo,
+                            @ApiParam(value = "退款金额") @RequestParam double amount,
+                            @ApiParam(value = "退款原因") @RequestParam(required = false) String refundReason) {
+
+        return wxPayService.refund(orderNo, amount, refundReason);
+    }*/
+    @ApiOperation(value = "统一下单-V3")
+    @GetMapping("/unifiedOrder2")
+    public RestResponse<AppPayData> unifiedOrder2(@ApiParam(value = "VIP商品主键") @RequestParam String vipGoodsId) {
+        AppPayData data = wxPayService.unifiedOrder2(vipGoodsId);
+        return RestResponse.ok(data);
+    }
+
+    @ApiOperation(value = "APP支付后调用,主要查询是否支付成功和计算VIP时长")
+    @GetMapping("/success")
+    public RestResponse<Object> success(@ApiParam(value = "VIP商品主键", example = "1")
+                                        @RequestParam("vipGoodsId") String vipGoodsId,
+                                        @ApiParam(value = "订单号", example = "out_trade_no的值")
+                                        @RequestParam("out_trade_no") String out_trade_no) {
+        SysUser sysUser = wxPayService.success(vipGoodsId, out_trade_no);
+        return RestResponse.ok(sysUser);
+    }
+
+    //查询订单
+    @ApiOperation(value = "查询订单号")
+    @GetMapping("/queryOrderByOutTradeNo")
+    public RestResponse<?> queryOrderByOutTradeNo(@ApiParam(value = "订单号", example = "out_trade_no的值") @RequestParam String out_trade_no) {
+        Transaction response = wxPayService.queryOrderByOutTradeNo(out_trade_no);
+        return RestResponse.ok(response);
+    }
+
+    // ============= 小程序支付相关接口 =============
+    
+    @ApiOperation(value = "小程序获取openid", notes = "通过小程序code获取用户openid")
+    @GetMapping("/miniprogram/getOpenid")
+    public RestResponse<Object> getMiniprogramOpenid(@ApiParam(value = "小程序code") @RequestParam String code) {
+        try {
+            String result = WxAppApiUtils.getMiniprogramOpenId(miniprogramAppId, miniprogramAppSecret, code);
+            return RestResponse.ok(result);
+        } catch (Exception e) {
+            log.error("获取小程序openid失败", e);
+            return RestResponse.error("获取openid失败:" + e.getMessage());
+        }
+    }
+    
+    @ApiOperation(value = "小程序统一下单", notes = "微信小程序支付统一下单")
+    @PostMapping("/miniprogram/unifiedOrder")
+    public RestResponse<Object> miniprogramUnifiedOrder(
+            @ApiParam(value = "VIP商品主键") @RequestParam String vipGoodsId,
+            @ApiParam(value = "用户openid") @RequestParam String openid) {
+        try {
+            Object payData = wxPayService.miniprogramUnifiedOrder(vipGoodsId, openid);
+            return RestResponse.ok(payData);
+        } catch (Exception e) {
+            log.error("小程序支付下单失败", e);
+            return RestResponse.error("支付下单失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "小程序支付成功后调用", notes = "小程序支付成功后查询订单状态并处理VIP时长")
+    @GetMapping("/miniprogram/success")
+    public RestResponse<Object> miniprogramSuccess(
+            @ApiParam(value = "VIP商品主键", example = "1") @RequestParam("vipGoodsId") String vipGoodsId,
+            @ApiParam(value = "订单号", example = "out_trade_no的值") @RequestParam("out_trade_no") String out_trade_no) {
+        try {
+            SysUser sysUser = wxPayService.miniprogramSuccess(vipGoodsId, out_trade_no);
+            return RestResponse.ok(sysUser);
+        } catch (Exception e) {
+            log.error("小程序支付成功处理失败", e);
+            return RestResponse.error("支付处理失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "小程序支付异步通知", notes = "微信小程序支付异步通知回调")
+    @PostMapping("/miniprogram/notify")
+    public String miniprogramPayNotify(HttpServletRequest request) {
+        InputStream is = null;
+        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml>";
+        try {
+            is = request.getInputStream();
+            // 将InputStream转换成String
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+            StringBuilder sb = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line + "\n");
+            }
+            xmlBack = wxPayService.miniprogramNotify(sb.toString());
+        } catch (Exception e) {
+            log.error("小程序支付回调通知失败:", e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return xmlBack;
+    }
+
+    // ============= 退款异步通知接口 =============
+    
+    @ApiOperation(value = "APP支付退款异步通知", notes = "微信APP支付退款异步通知回调")
+    @PostMapping("/refundNotify")
+    public String appRefundNotify(HttpServletRequest request) {
+        InputStream is = null;
+        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml>";
+        try {
+            is = request.getInputStream();
+            // 将InputStream转换成String
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+            StringBuilder sb = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line + "\n");
+            }
+            // 处理退款通知(可根据需要实现具体业务逻辑)
+            log.info("收到APP退款异步通知: {}", sb.toString());
+            xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
+        } catch (Exception e) {
+            log.error("APP退款回调通知失败:", e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return xmlBack;
+    }
+
+    @ApiOperation(value = "小程序支付退款异步通知", notes = "微信小程序支付退款异步通知回调")
+    @PostMapping("/miniprogram/refundNotify")
+    public String miniprogramRefundNotify(HttpServletRequest request) {
+        InputStream is = null;
+        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml>";
+        try {
+            is = request.getInputStream();
+            // 将InputStream转换成String
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+            StringBuilder sb = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line + "\n");
+            }
+            // 处理退款通知(可根据需要实现具体业务逻辑)
+            log.info("收到小程序退款异步通知: {}", sb.toString());
+            xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
+        } catch (Exception e) {
+            log.error("小程序退款回调通知失败:", e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return xmlBack;
+    }
+
+}

+ 73 - 0
src/main/java/com/dtb/portal/controller/view/AppView.java

@@ -0,0 +1,73 @@
+package com.dtb.portal.controller.view;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@ApiModel("应用")
+public class AppView implements Serializable {
+    @ApiModelProperty("主键")
+    private String id;
+
+    @ApiModelProperty("应用ID,对应Client的client_id")
+    private String clientId;
+
+    @ApiModelProperty("应用名称")
+    private String appName;
+
+    @ApiModelProperty("用户池ID")
+    private String userPoolId;
+
+    @ApiModelProperty("用户池名称")
+    private String userPoolName;
+
+    @ApiModelProperty("应用描述信息")
+    private String description;
+
+    @ApiModelProperty("应用Secret")
+    private String appSecret;
+
+    @ApiModelProperty("认证地址")
+    private String identifier;
+
+    @ApiModelProperty("回调地址")
+    private String redirectUris;
+
+    @ApiModelProperty(value = "应用logo文件名")
+    private String logo;
+
+
+    @ApiModelProperty("应用logo文件路径")
+    private String logoFilePath;
+
+    @ApiModelProperty("为 id_token 签名的 jwk")
+    private String jwks;
+
+    @ApiModelProperty("应用对应的协议类型")
+    private String protocol;
+
+    @ApiModelProperty("是否删除 1-删除 0-可用")
+    private int isDeleted = 0;
+
+    @ApiModelProperty("是否禁止注册 0-否 1-是")
+    private int registerDisabled = 0;
+
+    @ApiModelProperty("创建人")
+    private String createName;
+
+    @ApiModelProperty("更新人")
+    private String updateName;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty("更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}

+ 154 - 0
src/main/java/com/dtb/portal/controller/view/RestResponse.java

@@ -0,0 +1,154 @@
+package com.dtb.portal.controller.view;
+
+import com.dtb.portal.util.CodeDefault;
+import com.dtb.portal.util.CodeEnum;
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Rest 请求返回值
+ *
+ * @author zengfan
+ */
+@Data
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@JsonInclude(Include.NON_NULL)
+@JsonSerialize
+@JsonDeserialize
+@JsonClassDescription("API 返回对象")
+public class RestResponse<BEAN_CLASS> implements Serializable {
+
+    @JsonProperty
+    @JsonPropertyDescription("执行状态码, 执行成功返回 0, 其他的都是发生错误")
+    private long code;
+
+    @JsonProperty
+    @JsonPropertyDescription("消息, OK 的时候, message 一般不填")
+    private String message = null;
+
+    @JsonProperty
+    @JsonPropertyDescription("具体的数据, 发生错误的时候, data一般不填")
+    private BEAN_CLASS data = null;
+
+    @JsonProperty
+    @JsonPropertyDescription("时间戳, 只在 exception 时有用")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private Date timestamp = null;
+
+    @JsonProperty
+    @JsonPropertyDescription("放一些辅助信息 key->value, 一般用来进一步描述异常信息, OK 时暂未用到")
+    private Map<String, Object> extraInfo = null;
+
+
+    /**
+     * 表示调用成功
+     *
+     * @return 结果
+     */
+    public static <BEAN_CLASS> RestResponse<BEAN_CLASS> ok() {
+        RestResponse<BEAN_CLASS> restResponse = new RestResponse<>();
+        restResponse.code = CodeDefault.OK.getCode();
+        return restResponse;
+    }
+
+    /**
+     * 表示调用成功
+     *
+     * @param data 数据
+     * @return 结果
+     */
+    public static <BEAN_CLASS> RestResponse<BEAN_CLASS> ok(BEAN_CLASS data) {
+        RestResponse<BEAN_CLASS> restResponse = new RestResponse<>();
+        restResponse.code = CodeDefault.OK.getCode();
+        restResponse.data = data;
+        return restResponse;
+    }
+
+    /**
+     * 调用发生 异常
+     *
+     * @param errorMessage 代码枚举
+     * @return 结果
+     */
+    public static <BEAN_CLASS> RestResponse<BEAN_CLASS> timeOut(String errorMessage) {
+        RestResponse<BEAN_CLASS> restResponse = new RestResponse<>();
+        restResponse.code = -2;
+        restResponse.message = errorMessage;
+        restResponse.timestamp = new Date();
+        return restResponse;
+    }
+    /**
+     * 调用发生 异常
+     *
+     * @param errorMessage 代码枚举
+     * @return 结果
+     */
+    public static <BEAN_CLASS> RestResponse<BEAN_CLASS> noToken(String errorMessage) {
+        RestResponse<BEAN_CLASS> restResponse = new RestResponse<>();
+        restResponse.code = -3;
+        restResponse.message = errorMessage;
+        restResponse.timestamp = new Date();
+        return restResponse;
+    }
+    /**
+     * 调用发生 异常
+     *
+     * @param errorMessage 代码枚举
+     * @return 结果
+     */
+    public static <BEAN_CLASS> RestResponse<BEAN_CLASS> exception(String errorMessage) {
+        RestResponse<BEAN_CLASS> restResponse = new RestResponse<>();
+        restResponse.code = -1;
+        restResponse.message = errorMessage;
+        restResponse.timestamp = new Date();
+        return restResponse;
+    }
+    /**
+     * 调用发生 异常
+     *
+     * @param codeEnum 代码枚举
+     * @return 结果
+     */
+    public static <BEAN_CLASS> RestResponse<BEAN_CLASS> exception(CodeEnum codeEnum) {
+        return exception(codeEnum, null);
+    }
+
+    /**
+     * 调用发生 异常
+     *
+     * @param codeEnum      代码枚举
+     * @param exceptionInfo 详细信息
+     * @return 结果
+     */
+    public static <BEAN_CLASS> RestResponse<BEAN_CLASS> exception(CodeEnum codeEnum, Map<String, Object> exceptionInfo) {
+        RestResponse<BEAN_CLASS> restResponse = new RestResponse<>();
+        restResponse.code = codeEnum.getCode();
+        restResponse.message = codeEnum.getDefaultMessage();
+        restResponse.timestamp = new Date();
+        restResponse.extraInfo = exceptionInfo;
+        return restResponse;
+    }
+
+    /**
+     * 调用发生 异常
+     *
+     * @param errorMessage 错误信息
+     * @return 结果
+     */
+    public static <BEAN_CLASS> RestResponse<BEAN_CLASS> error(String errorMessage) {
+        RestResponse<BEAN_CLASS> restResponse = new RestResponse<>();
+        restResponse.code = -1;
+        restResponse.message = errorMessage;
+        restResponse.timestamp = new Date();
+        return restResponse;
+    }
+}

+ 59 - 0
src/main/java/com/dtb/portal/controller/view/pageable/Page.java

@@ -0,0 +1,59 @@
+package com.dtb.portal.controller.view.pageable;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 分页对象
+ *
+ * @param <T>
+ */
+@NoArgsConstructor
+@Data
+@ApiModel
+public class Page<T> {
+
+    @ApiModelProperty(value = "当前页,默认第一页")
+    private Integer currentPage = 1;
+
+    @ApiModelProperty(value = "每页显示的总条数,默认10条")
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "总记录数")
+    private Integer totalNum;
+
+    @ApiModelProperty(value = "是否有下一页")
+    private Integer hasNext;
+
+    @ApiModelProperty(value = "总页数")
+    private Integer totalPage;
+
+    @ApiModelProperty(value = "起始索引")
+    private Integer startIndex;
+
+    @ApiModelProperty(value = "分页列表")
+    private List<T> list;
+
+    public Page(Integer currentPage, Integer pageSize, Integer totalNum) {
+        super();
+        this.currentPage = currentPage;
+        this.pageSize = pageSize;
+        this.totalNum = totalNum;
+        this.totalPage = (this.totalNum + this.pageSize - 1) / this.pageSize;
+        this.startIndex = (this.currentPage - 1) * this.pageSize;
+        this.hasNext = this.currentPage >= this.totalPage ? 0 : 1;
+    }
+
+    public void resetPage(Integer currentPage, Integer pageSize, Integer totalNum){
+        this.currentPage = currentPage;
+        this.pageSize = pageSize;
+        this.totalNum = totalNum;
+        this.totalPage = (this.totalNum + this.pageSize - 1) / this.pageSize;
+        this.startIndex = (this.currentPage - 1) * this.pageSize;
+        this.hasNext = this.currentPage >= this.totalPage ? 0 : 1;
+    }
+}

+ 41 - 0
src/main/java/com/dtb/portal/controller/view/pageable/PageQuery.java

@@ -0,0 +1,41 @@
+package com.dtb.portal.controller.view.pageable;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+/**
+ * 分页查询对象
+ *
+ * @param <T>
+ */
+@Data
+@NoArgsConstructor
+@ApiModel
+@Accessors(chain = true)
+public class PageQuery<T> {
+
+    @ApiModelProperty(value = "当前页,默认第一页")
+    private Integer currentPage = 1;
+
+    @ApiModelProperty(value = "每页显示的总条数,默认10条", position = 1)
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "排序[字段名+空格+排序规律],例(create_on DESC);支持create_on、update_on、order_by", position = 2)
+    private String orderBy;
+
+    @ApiModelProperty(value = "查询对象", position = 3)
+    private T queryBean;
+
+    public void check() {
+        if (this.getCurrentPage() < 1) {
+            this.setCurrentPage(1);
+        }
+        if (this.getPageSize() < 1) {
+            throw new RuntimeException("分页参数错误");
+        }
+    }
+
+}

+ 57 - 0
src/main/java/com/dtb/portal/controller/view/request/UserAddByNamePassRequest.java

@@ -0,0 +1,57 @@
+package com.dtb.portal.controller.view.request;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.util.Date;
+
+import javax.validation.constraints.NotBlank;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class UserAddByNamePassRequest {
+
+    @ApiModelProperty(value = "微信小程序的openid值")
+    private String openId;
+
+    @ApiModelProperty(value = "用户类型", example = "1:管理员 2:微信用户")
+    private String type;
+
+    @ApiModelProperty("logo图像路径")
+    private String logoPath;
+
+    @ApiModelProperty(value = "用户名", required = true)
+    @NotBlank(message = "用户名不能为空")
+    private String accountName;
+
+    @ApiModelProperty("昵称")
+    private String nickname;
+
+    @ApiModelProperty(value = "密码",required = true)
+    @NotBlank(message = "用户名不能为空")
+    private String password;
+
+    @ApiModelProperty(value = "手机号",required = true)
+    @NotBlank(message = "手机号不能为空")
+    private String phone;
+    @ApiModelProperty(value = "验证码",required = true)
+    @NotBlank(message = "验证码不能为空")
+    private String checkCode;
+
+    @ApiModelProperty(value = "是否启用", example = "启用:true 禁用:false")
+    private String enable;
+
+    @ApiModelProperty(value = "性别", example = "1:男 2:女")
+    private String gender;
+
+    @ApiModelProperty("地区")
+    private String location;
+    @ApiModelProperty("国家")
+    private String nation;
+
+    @ApiModelProperty("生日")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
+    private Date birthday;
+}

+ 49 - 0
src/main/java/com/dtb/portal/controller/view/request/UserAddRequest.java

@@ -0,0 +1,49 @@
+package com.dtb.portal.controller.view.request;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.util.Date;
+
+@Data
+public class UserAddRequest {
+
+    @ApiModelProperty(value = "微信小程序的openid值", required = true)
+    @NotBlank(message = "微信小程序的openid不能为空")
+    private String openId;
+
+    @ApiModelProperty(value = "用户类型", example = "1:管理员 2:微信用户")
+    private String type;
+
+    @ApiModelProperty("logo图像路径")
+    private String logoPath;
+
+    @ApiModelProperty(value = "用户名", required = true)
+    @NotBlank(message = "用户名不能为空")
+    private String accountName;
+
+    @ApiModelProperty("昵称")
+    private String nickname;
+
+    @ApiModelProperty(value = "手机号",required = true)
+    @NotBlank(message = "手机号不能为空")
+    private String phone;
+
+    @ApiModelProperty(value = "是否启用", example = "启用:true 禁用:false")
+    private String enable;
+
+    @ApiModelProperty(value = "性别", example = "1:男 2:女")
+    private String gender;
+
+    @ApiModelProperty("地区")
+    private String location;
+    @ApiModelProperty("国家")
+    private String nation;
+
+    @ApiModelProperty("生日")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
+    private Date birthday;
+}

+ 16 - 0
src/main/java/com/dtb/portal/controller/view/request/UserRemoveNotCheckRequest.java

@@ -0,0 +1,16 @@
+package com.dtb.portal.controller.view.request;
+
+
+import javax.validation.constraints.NotBlank;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class UserRemoveNotCheckRequest {
+
+    @ApiModelProperty(value = "主键", required = true)
+    @NotBlank(message = "主键不能为空")
+    private String id;
+
+}

+ 27 - 0
src/main/java/com/dtb/portal/controller/view/request/UserRemoveRequest.java

@@ -0,0 +1,27 @@
+package com.dtb.portal.controller.view.request;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.util.Date;
+
+import javax.validation.constraints.NotBlank;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class UserRemoveRequest {
+
+
+    @ApiModelProperty(value = "主键", required = true)
+    @NotBlank(message = "主键不能为空")
+    private String id;
+
+    @ApiModelProperty(value = "验证码", required = true)
+    @NotBlank(message = "验证码不能为空")
+    private String checkCode;
+
+
+
+}

+ 22 - 0
src/main/java/com/dtb/portal/controller/view/request/UserRequest.java

@@ -0,0 +1,22 @@
+package com.dtb.portal.controller.view.request;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("用户表")
+public class UserRequest {
+
+    @ApiModelProperty("用户名")
+    private String accountName;
+
+    @ApiModelProperty("手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "是否启用",example = "启用:true 禁用:false")
+    private String enable;
+
+
+}

+ 55 - 0
src/main/java/com/dtb/portal/controller/view/request/UserUpdateRequest.java

@@ -0,0 +1,55 @@
+package com.dtb.portal.controller.view.request;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.util.Date;
+
+@Data
+public class UserUpdateRequest {
+
+
+    @ApiModelProperty(value = "主键", required = true)
+    @NotBlank(message = "主键不能为空")
+    private String id;
+
+    @ApiModelProperty("微信小程序的openid值")
+    private String openId;
+
+    @ApiModelProperty(value = "用户类型", example = "1:管理员 2:微信用户")
+    private String type;
+
+    @ApiModelProperty("logo图像路径")
+    private String logoPath;
+
+    @ApiModelProperty("用户名")
+    private String accountName;
+
+    @ApiModelProperty("密码")
+    private String password;
+
+    @ApiModelProperty("昵称")
+    private String nickname;
+
+    @ApiModelProperty("手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "是否启用", example = "启用:true 禁用:false")
+    private String enable;
+
+    @ApiModelProperty(value = "性别", example = "1:男 2:女")
+    private String gender;
+
+    @ApiModelProperty("地区")
+    private String location;
+
+    @ApiModelProperty("国家")
+    private String nation;
+
+    @ApiModelProperty("生日")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
+    private Date birthday;
+}

+ 15 - 0
src/main/java/com/dtb/portal/controller/view/request/VideoRequest.java

@@ -0,0 +1,15 @@
+package com.dtb.portal.controller.view.request;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("视频表")
+public class VideoRequest {
+
+    @ApiModelProperty("关键字")
+    private String keyWord;
+
+}

+ 12 - 0
src/main/java/com/dtb/portal/controller/view/request/VideoUserRequest.java

@@ -0,0 +1,12 @@
+package com.dtb.portal.controller.view.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class VideoUserRequest {
+    @ApiModelProperty("视频名称")
+    private String name;
+    @ApiModelProperty("视频内容")
+    private String content;
+} 

+ 20 - 0
src/main/java/com/dtb/portal/controller/view/request/VideoUserUpdateRequest.java

@@ -0,0 +1,20 @@
+package com.dtb.portal.controller.view.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class VideoUserUpdateRequest {
+    @ApiModelProperty("主键")
+    private String id;
+    @ApiModelProperty("视频名称")
+    private String name;
+    @ApiModelProperty("头像路径")
+    private String headPath;
+    @ApiModelProperty("视频路径")
+    private String videoPath;
+    @ApiModelProperty("视频封面")
+    private String videoImg;
+    @ApiModelProperty("内容")
+    private String content;
+} 

+ 29 - 0
src/main/java/com/dtb/portal/controller/view/request/VipGoodsRequest.java

@@ -0,0 +1,29 @@
+package com.dtb.portal.controller.view.request;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class VipGoodsRequest {
+
+
+    @ApiModelProperty("商品主键")
+    private String id;
+
+    @ApiModelProperty(value = "商品名称",required = true)
+    private String name;
+
+    @ApiModelProperty(value = "支付方式", example = "1:微信 2:其他",required = true)
+    private String payMode;
+
+    @ApiModelProperty(value = "VIP特权",required = true)
+    private String privilege;
+
+    @ApiModelProperty(value = "会员时长",required = true)
+    private String vipDate;
+
+    @ApiModelProperty(value = "会员金额",required = true)
+    private String vipMoney;
+
+}

+ 21 - 0
src/main/java/com/dtb/portal/controller/view/response/CountAndNameResponse.java

@@ -0,0 +1,21 @@
+package com.dtb.portal.controller.view.response;
+
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class CountAndNameResponse {
+
+
+    @ApiModelProperty("登录的次数")
+    private Integer count;
+
+    @ApiModelProperty("应用的名称")
+    private String appName;
+
+    @ApiModelProperty("应用主键")
+    @JsonIgnore
+    private String appId;
+}

+ 56 - 0
src/main/java/com/dtb/portal/controller/view/response/ResultMap.java

@@ -0,0 +1,56 @@
+package com.dtb.portal.controller.view.response;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Description 通用返回结果集
+ * @Author
+ * @Date 2018/6/12 15:13
+ */
+public class ResultMap extends HashMap<String, Object> {
+    public ResultMap() {
+        put("state", true);
+        put("code", 0);
+        put("msg", "success");
+    }
+
+    public static ResultMap error(int code, String msg) {
+        ResultMap r = new ResultMap();
+        r.put("state", false);
+        r.put("code", code);
+        r.put("msg", msg);
+        return r;
+    }
+
+    public static ResultMap error(String msg) {
+        return error(0, msg);
+    }
+
+    public static ResultMap error() {
+        return error(1, "未知异常,请联系管理员");
+    }
+
+    public static ResultMap ok(String msg) {
+        ResultMap r = new ResultMap();
+        r.put("msg", msg);
+        return r;
+    }
+
+    public static ResultMap ok(Map<String, Object> par) {
+        ResultMap r = new ResultMap();
+        r.putAll(par);
+        return r;
+    }
+
+    public static ResultMap ok() {
+        return new ResultMap();
+    }
+
+    public ResultMap put(String key, Object value) {
+        super.put(key, value);
+        return this;
+    }
+
+}

+ 102 - 0
src/main/java/com/dtb/portal/controller/view/response/UserPoolView.java

@@ -0,0 +1,102 @@
+package com.dtb.portal.controller.view.response;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.io.File;
+import java.util.Date;
+import java.util.List;
+
+
+@Data
+public class UserPoolView {
+    @ApiModelProperty("主键")
+    private String id;
+
+    @ApiModelProperty("创建人ID")
+    private String userId;
+
+    @ApiModelProperty("用户池名称")
+    @NotBlank(message = "用户池名称不能为空")
+    @Size(min = 5, max = 20, message = "用户池名称长度为5-20字符")
+    private String name;
+
+    @ApiModelProperty("状态 0-可用 1-不可用")
+    private String status;
+
+    @ApiModelProperty("用户池密钥")
+    private String userpoolSecret;
+
+    @ApiModelProperty("认证地址")
+    private String authAddress;
+
+    @ApiModelProperty(value = "用户池LOGO", required = true)
+    //@NotBlank(message = "用户池LOGO不能为空")
+    private String logo;
+
+    @ApiModelProperty("用户池LOGO图片文件")
+    private File logoFile;
+
+    @ApiModelProperty(value = "用户池LOGO路径", required = true)
+    @NotBlank(message = "用户池LOGO不能为空")
+    private String logoPath;
+
+    @ApiModelProperty("用户池描述信息")
+    private String descri;
+
+    @ApiModelProperty("用户池地址")
+    private String userpoolAddress;
+
+    @ApiModelProperty("创建人")
+    private String createName;
+
+    @ApiModelProperty("更新人")
+    private String updateName;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private Date createTime;
+
+    @ApiModelProperty("更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private Date updateTime;
+
+    @ApiModelProperty("白名单用户")
+    private List<String> whiteUserList;
+
+    @ApiModelProperty("白名单用户")
+    @JsonIgnore
+    private String whiteUser;
+
+    @ApiModelProperty("JWT有效时间,默认一个小时")
+    private String jwtValidity = "1";
+
+    @ApiModelProperty("验证码长度,默认是是4个纯数字")
+    private Integer codeLength = 4;
+
+    @ApiModelProperty(value = "是否禁止未验证邮箱用户登录,默认是未禁止:false", example = "禁止:true 未禁止:false")
+    private Boolean isLockEmail = false;
+
+    @ApiModelProperty(value = "登录失败次数", example = "默认是3次")
+    private Integer loginFailCount = 3;
+
+    @ApiModelProperty(value = "登录密码错误限制", example = "默认是3次")
+    private Integer loginPassErrorCount = 3;
+
+    @ApiModelProperty(value = "是否开启频繁注册", example = "false:未开启 true:开启")
+    private Boolean isFrequentlyRegister = false;
+
+    @ApiModelProperty(value = "注册方式", example = "1:用户名-密码 2:邮箱-密码")
+    private Integer registerMode = 1;
+
+    @ApiModelProperty(value = "是否允许注册,默认是:true", example = "false:不允许 true:允许")
+    private Boolean noRegister = true;
+
+    @ApiModelProperty(value = "个性化配置登录主题风格", example = "1:默认 2:主题2")
+    private Integer personalization;
+
+}

+ 27 - 0
src/main/java/com/dtb/portal/controller/view/response/UserSecurityResponse.java

@@ -0,0 +1,27 @@
+package com.dtb.portal.controller.view.response;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author fuzhiyou
+ * @date 2022-04-08 18:07
+ */
+@Data
+@ApiModel("安全中心")
+public class UserSecurityResponse {
+
+
+    @ApiModelProperty("用户ID")
+    private String userId;
+
+    @ApiModelProperty("密码强度 0:弱 1:中 2:强")
+    private int passStrength;
+
+    @ApiModelProperty("手机号绑定 0:已经绑定 1:未绑定")
+    private int pnoneBinding=1;
+
+    @ApiModelProperty("邮箱绑定 0:已经绑定 1:未绑定")
+    private int emailBinding=1;
+}

+ 28 - 0
src/main/java/com/dtb/portal/dto/VideoUploadDTO.java

@@ -0,0 +1,28 @@
+package com.dtb.portal.dto;
+
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 绑定的设备信息
+ *
+ * @author:slambb
+ * @date:2020/6/12
+ */
+@Data
+public class VideoUploadDTO {
+    @NotNull(message = "上传图片url不能为空")
+    private String imageUrl;
+    @NotNull(message = "系列名称不能为空")
+    private String title;
+    @NotNull(message = "视频标题不能为空")
+    private String subtitle;
+    @NotNull(message = "视频简介不能为空")
+    private String content;
+    @NotNull(message = "上传视频不能为空")
+    private MultipartFile file;
+    @NotNull(message = "视频序列号不能为空")
+    private String num;
+}

+ 49 - 0
src/main/java/com/dtb/portal/entity/AppPayData.java

@@ -0,0 +1,49 @@
+package com.dtb.portal.entity;
+
+
+import com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@ApiModel("APP调起支付返回对象")
+@Data
+public class AppPayData {
+
+
+    @ApiModelProperty("微信开放平台审核通过的移动应用AppID")
+    private String appid;
+
+    @ApiModelProperty("请填写商户号mchid对应的值")
+    private String partnerid;
+
+
+    @ApiModelProperty("微信返回的支付交易会话ID,该值有效期为2小时")
+    private String prepayid;
+
+    @ApiModelProperty("原来的签名,使用字段AppID、timeStamp、nonceStr、prepayid计算得出的签名值 注意:取值RSA格式")
+    private String sign;
+
+    @ApiModelProperty("base64签名,使用字段AppID、timeStamp、nonceStr、prepayid计算得出的签名值 注意:取值RSA格式")
+    private String base64Sign;
+
+    @ApiModelProperty("随机字符串,不长于32位。推荐随机数生成算法")
+    private String noncestr;
+
+    @ApiModelProperty("时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。")
+    private String timestamp;
+
+    @ApiModelProperty("暂填写固定值Sign=WXPay")
+    private String package2;
+
+    private String signSHA256;
+
+    private String base64SignSHA256;
+
+    private String signMD5;
+
+    private String base64SignMD5;
+
+
+    private PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse;
+}

+ 71 - 0
src/main/java/com/dtb/portal/entity/MiniprogramPayData.java

@@ -0,0 +1,71 @@
+package com.dtb.portal.entity;
+
+import lombok.Data;
+
+/**
+ * 小程序支付数据传输对象
+ */
+@Data
+public class MiniprogramPayData {
+    
+    /**
+     * 时间戳
+     */
+    private String timeStamp;
+    
+    /**
+     * 随机字符串
+     */
+    private String nonceStr;
+    
+    /**
+     * 订单详情扩展字符串
+     */
+    private String package_;
+    
+    /**
+     * 订单详情扩展字符串(用于uni.requestPayment)
+     */
+    @com.fasterxml.jackson.annotation.JsonProperty("package")
+    private String packageParam;
+    
+    /**
+     * 支付提供商(uni-app需要)
+     */
+    private String provider = "wxpay";
+    
+    /**
+     * 签名方式
+     */
+    private String signType;
+    
+    /**
+     * 签名
+     */
+    private String paySign;
+    
+    /**
+     * 订单号
+     */
+    private String out_trade_no;
+    
+    /**
+     * 预支付交易会话标识
+     */
+    private String prepay_id;
+    
+    /**
+     * 商品描述
+     */
+    private String description;
+    
+    /**
+     * 支付金额(分)
+     */
+    private Integer total;
+    
+    /**
+     * 微信支付返回的原始数据(调试用)
+     */
+    private Object rawData;
+}

+ 76 - 0
src/main/java/com/dtb/portal/entity/SysUser.java

@@ -0,0 +1,76 @@
+package com.dtb.portal.entity;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ApiModel("用户实体类")
+public class SysUser {
+
+    @ApiModelProperty("主键")
+    private String id;
+
+    @ApiModelProperty("微信小程序的openid值")
+    private String openId;
+
+    @ApiModelProperty(value = "用户类型", example = "1:管理员 2:微信用户")
+    private String type;
+
+    @ApiModelProperty("logo图像路径")
+    private String logoPath;
+
+    @ApiModelProperty("用户名")
+    private String accountName;
+
+    @ApiModelProperty("昵称")
+    private String nickname;
+
+    @ApiModelProperty("密码")
+    private String password;
+
+    @ApiModelProperty("手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "是否启用", example = "启用:true 禁用:false")
+    private Boolean enable;
+
+    @ApiModelProperty(value = "性别", example = "1:男 2:女")
+    private String gender;
+
+    @ApiModelProperty("地区")
+    private String location;
+
+    @ApiModelProperty("国家")
+    private String nation;
+
+    @ApiModelProperty("生日")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
+    private Date birthday;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private Date createTime;
+
+    @ApiModelProperty("修改时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+    private Date updateTime;
+
+    private Boolean newUser;
+
+    private String token;
+
+    @ApiModelProperty(value = "是否是VIP用户", example = "1:是 2:不是")
+    private Boolean isVip;
+
+    @ApiModelProperty("VIP剩余天数")
+    private Long vipResidueDate;
+
+    @ApiModelProperty("VIP结束时间")
+    private String vipEndDate;
+
+
+}

+ 42 - 0
src/main/java/com/dtb/portal/entity/Video.java

@@ -0,0 +1,42 @@
+package com.dtb.portal.entity;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Data
+@ApiModel("视频上传实体类")
+public class Video {
+    @ApiModelProperty("系列名称")
+    private String title;
+
+    @ApiModelProperty(value = "视频名称")
+    private String subtitle;
+
+    @ApiModelProperty("视频序列号")
+    private String num;
+
+    @ApiModelProperty("视频路径")
+    private String videoPath;
+
+    @ApiModelProperty("图片路径")
+    private String videoImg;
+
+    @ApiModelProperty("简介")
+    private String content;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone = "Asia/Shanghai")
+    private Date createTime;
+
+    @ApiModelProperty("更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone = "Asia/Shanghai")
+    private Date updateTime;
+
+
+
+}

+ 17 - 0
src/main/java/com/dtb/portal/entity/VideoUser.java

@@ -0,0 +1,17 @@
+package com.dtb.portal.entity;
+
+import lombok.Data;
+import java.util.Date;
+
+@Data
+public class VideoUser {
+    private String id;
+    private String title;        // 改为 title,对应数据库字段
+    private String subtitle;     // 改为 subtitle,对应数据库字段
+    private String num;          // 改为 num,对应数据库字段
+    private String videoPath;    // 对应 video_path
+    private String videoImg;     // 对应 video_img
+    private String content;      // 对应 content
+    private Date updateTime;     // 对应 update_time
+    private Date createTime;     // 对应 create_time
+} 

+ 23 - 0
src/main/java/com/dtb/portal/entity/VipGoodsData.java

@@ -0,0 +1,23 @@
+package com.dtb.portal.entity;
+
+import com.dtb.portal.controller.view.request.VipGoodsRequest;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class VipGoodsData extends VipGoodsRequest {
+
+    @ApiModelProperty("主键")
+    private String id;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone = "Asia/Shanghai")
+    private LocalDateTime updateTime;
+}

+ 35 - 0
src/main/java/com/dtb/portal/entity/VipOrderData.java

@@ -0,0 +1,35 @@
+package com.dtb.portal.entity;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class VipOrderData {
+
+    @ApiModelProperty("主键")
+    private String id;
+
+    @ApiModelProperty("订单号")
+    private String outTradeNo;
+
+    @ApiModelProperty("VIP商品主键")
+    private String vipGoodsId;
+
+    @ApiModelProperty("用户ID")
+    private String userId;
+
+    @ApiModelProperty("用户openid")
+    private String openid;
+
+    @ApiModelProperty(value = "支付状态",example = "1:预支付 2:支付成功 3:支付失败")
+    private String status;
+
+    @ApiModelProperty("创建时间")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("更新时间")
+    private LocalDateTime updateTime;
+}

+ 37 - 0
src/main/java/com/dtb/portal/entity/VipUserData.java

@@ -0,0 +1,37 @@
+package com.dtb.portal.entity;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class VipUserData {
+
+    @ApiModelProperty("主键")
+    private String id;
+
+    @ApiModelProperty("用户主键")
+    private String userId;
+
+    @ApiModelProperty("vip商品主键")
+    private String vipGoodsId;
+
+    @ApiModelProperty("开通时间")
+    private String vipStartDate;
+
+    @ApiModelProperty("结束时间")
+    private String vipEndDate;
+
+    @ApiModelProperty("剩余天数")
+    private String vipResidueDate;
+
+    @ApiModelProperty("创建时间")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("更新时间")
+    private LocalDateTime updateTime;
+
+
+}

+ 68 - 0
src/main/java/com/dtb/portal/exception/GlobalException.java

@@ -0,0 +1,68 @@
+package com.dtb.portal.exception;
+
+
+
+import com.dtb.portal.config.interceptor.CodeEnum;
+import com.google.common.collect.Maps;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Map;
+
+/**
+ * 全局 exception, 本系统中的所有自定义 exception 都应该由此派生, 以便 GlobalExceptionHandler 进行捕获
+ *
+ *
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class GlobalException extends RuntimeException {
+
+    private CodeEnum codeEnum;
+
+    private Map<String, Object> extraInfo = Maps.newHashMap();
+
+    private GlobalException() {
+        super();
+    }
+
+    /**
+     * 不允许使用不带 Throwable cause 的构造函数,如果没有 case,就传入 null
+     * 这样可以避免在有 Throwable cause 时,也漏传的情况
+     *
+     * @param codeEnum
+     */
+    private GlobalException(CodeEnum codeEnum) {
+        super(codeEnum.toString());
+        this.codeEnum = codeEnum;
+    }
+
+    /**
+     * 使用 codeEnum 的 defaultMessage 异常信息
+     * defaultMessage 用来输出国际化信息
+     * 一定要带上 cause,才能 log 出最原始的堆栈
+     *
+     * @param codeEnum
+     */
+    public GlobalException(CodeEnum codeEnum, Throwable cause) {
+        super(codeEnum.toString(), cause);
+        this.codeEnum = codeEnum;
+    }
+
+    /**
+     * 使用 defaultMessage + extraInfo 作为异常信息
+     * defaultMessage 用来输出国际化信息
+     * 一定要带上 cause,才能 log 出最原始的堆栈
+     *
+     * @param codeEnum
+     * @param extraInfo
+     */
+    public GlobalException(CodeEnum codeEnum, Map<String, Object> extraInfo, Throwable cause) {
+        super(String.format("%s\t%s", codeEnum.toString(), extraInfo == null ? "" : extraInfo.toString()), cause);
+        this.codeEnum = codeEnum;
+        if (null != extraInfo) {
+            this.extraInfo.putAll(extraInfo);
+        }
+    }
+}
+

+ 113 - 0
src/main/java/com/dtb/portal/exception/GlobalExceptionHandler.java

@@ -0,0 +1,113 @@
+package com.dtb.portal.exception;
+
+
+
+import com.dtb.portal.controller.view.RestResponse;
+import com.dtb.portal.util.CodeDefault;
+import com.google.common.base.Strings;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.ServletRequestBindingException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.servlet.http.HttpServletRequest;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 全局异常处理
+ */
+@RestControllerAdvice
+@ControllerAdvice
+@Slf4j
+public class GlobalExceptionHandler {
+
+    /**
+     * controller 参数转化时, 主要从这里捕获错误信息
+     *
+     * @param request 请求
+     * @param e       错误信息
+     * @return 请求结果
+     */
+    @ExceptionHandler(value = ServletRequestBindingException.class)
+    @ResponseBody
+    public RestResponse<?> exception(HttpServletRequest request, ServletRequestBindingException e) {
+        RestResponse<?> exception = RestResponse.exception(CodeDefault.ILLEGAL_ARGUMENT);
+        log.error("请求参数错误: {}", exception, e);
+        return exception;
+    }
+
+    /**
+     * NPE处理
+     *
+     * @param request 请求
+     * @param npe     错误信息
+     * @return 请求结果
+     */
+    @ExceptionHandler(value = NullPointerException.class)
+    @ResponseBody
+    public RestResponse<?> exception(HttpServletRequest request, NullPointerException npe) {
+      /*  RestResponse<?> exception = RestResponse.exception(CodeDefault.NULL_POINT_ERROR);
+        if (!Strings.isNullOrEmpty(npe.getMessage())) {
+            exception.setMessage(npe.getMessage());
+        }
+        Map<String, Object> extras = new HashMap<>(0);
+        StackTraceElement stackTraceElement = npe.getStackTrace()[0];
+        extras.put("class", stackTraceElement.getClassName());
+        extras.put("method", stackTraceElement.getMethodName());
+        extras.put("lineNumber", stackTraceElement.getLineNumber());
+        exception.setExtraInfo(extras);
+        log.error("NullPointerException: {}", exception, npe);*/
+        String errMessage = npe.getMessage();
+        if (StringUtils.isNotBlank(errMessage)) {
+            errMessage = errMessage.split(":")[1].replaceAll(" ","");
+        }
+        return RestResponse.ok(errMessage);
+    }
+
+    /**
+     * 这个兜底
+     *
+     * @param request 请求
+     * @param e       错误信息
+     * @return 请求结果
+     */
+    @ExceptionHandler(value = RuntimeException.class)
+    @ResponseBody
+    public RestResponse<?> exception(HttpServletRequest request, RuntimeException e) {
+        String errMessage = e.getMessage();
+        log.error("RuntimeException :",errMessage);
+    /*    if (StringUtils.isNotBlank(errMessage)) {
+            errMessage = errMessage.split(":")[1].replaceAll(" ","");
+        }*/
+            if (errMessage.equals("会话超时,请重新登录")) {
+                return RestResponse.timeOut(errMessage);
+        }
+            if (errMessage.equals("验证失败,请重新登录后使用")) {
+                return RestResponse.timeOut(errMessage);
+        }
+            if (errMessage.equals("未登录")) {
+                return RestResponse.noToken(errMessage);
+        }
+
+
+
+        return RestResponse.exception(errMessage);
+    }
+
+    @ExceptionHandler(value = Exception.class)
+    @ResponseBody
+    public RestResponse<?> exception(HttpServletRequest request, Exception e) {
+        String errMessage = e.getMessage();
+     /*   if (StringUtils.isNotBlank(errMessage)) {
+            errMessage = errMessage.split(":")[1].replaceAll(" ","");
+        }*/
+        log.error("==============>Exception :",e);
+        return RestResponse.exception(errMessage);
+    }
+}

+ 34 - 0
src/main/java/com/dtb/portal/mapper/SysUserMapper.java

@@ -0,0 +1,34 @@
+package com.dtb.portal.mapper;
+
+
+import com.dtb.portal.controller.view.request.UserRequest;
+import com.dtb.portal.entity.SysUser;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface SysUserMapper {
+
+
+    List<SysUser> page(UserRequest queryBean);
+
+    SysUser selectOne(String id);
+
+    void updateEnable(SysUser sysUser);
+
+    void updatePassword(SysUser sysUser);
+
+    SysUser selectByAccountName(String accountName);
+
+    SysUser selectByOpenId(String openId);
+
+    void update(SysUser sysUser);
+
+    void save(SysUser user);
+
+    SysUser selectByPhone(String phone);
+
+    void delete(String id);
+}
+

+ 25 - 0
src/main/java/com/dtb/portal/mapper/VideoMapper.java

@@ -0,0 +1,25 @@
+package com.dtb.portal.mapper;
+
+
+import com.dtb.portal.controller.view.request.UserRequest;
+import com.dtb.portal.entity.SysUser;
+import com.dtb.portal.entity.Video;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface VideoMapper {
+
+
+    List<Video> page(String keyWord);
+
+    Video selectOne(String id);
+
+    void updateEnable(Video video);
+
+    void save(Video video);
+
+    void delete(String id);
+}
+

+ 16 - 0
src/main/java/com/dtb/portal/mapper/VideoUserMapper.java

@@ -0,0 +1,16 @@
+package com.dtb.portal.mapper;
+
+import com.dtb.portal.entity.VideoUser;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface VideoUserMapper {
+    int insert(VideoUser videoUser);
+    int update(VideoUser videoUser);
+    int deleteById(@Param("id") String id);
+    VideoUser selectById(@Param("id") String id);
+    List<VideoUser> selectList(@Param("title") String title, @Param("content") String content);
+} 

+ 39 - 0
src/main/java/com/dtb/portal/mapper/VipGoodsMapper.java

@@ -0,0 +1,39 @@
+package com.dtb.portal.mapper;
+
+
+import com.dtb.portal.controller.view.request.VipGoodsRequest;
+import com.dtb.portal.entity.VipGoodsData;
+import com.dtb.portal.entity.VipOrderData;
+import com.dtb.portal.entity.VipUserData;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface VipGoodsMapper {
+    void insert(VipGoodsData data);
+
+    VipGoodsData selectByName(@Param("name") String name,@Param("id") String id);
+
+    void update(VipGoodsData data);
+
+    void delete(String id);
+
+    VipGoodsData selectById(String id);
+
+    List<VipGoodsData> selectPage(VipGoodsRequest request);
+
+    VipUserData selectByUserId(@Param("userId") String userId, @Param("vipGoodsId") String vipGoodsId);
+
+    void insertVipUser(VipUserData saveVipUserData);
+
+    void deleteVipUser(String id);
+
+    void insertVipOrderData(VipOrderData data);
+
+    VipOrderData selectByOutTradeNo(String outTradeNo);
+
+    void updateVipOrderData(VipOrderData data);
+
+}

+ 16 - 0
src/main/java/com/dtb/portal/service/VideoUserService.java

@@ -0,0 +1,16 @@
+package com.dtb.portal.service;
+
+import com.dtb.portal.entity.VideoUser;
+import com.dtb.portal.controller.view.request.VideoUserRequest;
+import com.dtb.portal.controller.view.request.VideoUserUpdateRequest;
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.github.pagehelper.PageInfo;
+
+public interface VideoUserService {
+    VideoUser add(VideoUserRequest request);
+    VideoUser update(VideoUserUpdateRequest request);
+    void delete(String id);
+    VideoUser get(String id);
+    PageInfo<VideoUser> page(PageQuery<VideoUserRequest> pageQuery);
+    java.util.List<VideoUser> list(String name, String content);
+} 

+ 33 - 0
src/main/java/com/dtb/portal/service/VipGoodsService.java

@@ -0,0 +1,33 @@
+package com.dtb.portal.service;
+
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.controller.view.request.VipGoodsRequest;
+import com.dtb.portal.entity.VipGoodsData;
+import com.dtb.portal.entity.VipOrderData;
+import com.dtb.portal.entity.VipUserData;
+import com.github.pagehelper.PageInfo;
+
+public interface VipGoodsService {
+    VipGoodsData add(VipGoodsRequest request);
+
+    VipGoodsData update(VipGoodsRequest request);
+
+    VipGoodsData delete(String id);
+
+    PageInfo<VipGoodsData> page(PageQuery<VipGoodsRequest> pageQuery);
+
+    VipUserData selectByUserId(String userId, String vipGoodsId);
+
+    VipGoodsData getById(String vipGoodsId);
+
+    void saveVipUserData(VipUserData saveVipUserData);
+
+    void deleteVipUser(String id);
+
+    void insertVipOrderData(VipOrderData data);
+
+    VipOrderData getOrderByOutTradeNo(String outTradeNo);
+
+    void updateVipOrderData(VipOrderData data);
+
+}

+ 74 - 0
src/main/java/com/dtb/portal/service/WxPayService.java

@@ -0,0 +1,74 @@
+package com.dtb.portal.service;
+
+
+import com.dtb.portal.controller.view.response.ResultMap;
+import com.dtb.portal.entity.AppPayData;
+import com.dtb.portal.entity.SysUser;
+import com.wechat.pay.java.service.payments.model.Transaction;
+
+/**
+ * 微信支付服务接口
+ */
+public interface WxPayService {
+
+    /**
+     * @param vipGoodsId : VIP商品主键
+     * @return
+     * @Description: 微信支付统一下单
+     * @Author:
+     * @Date: 2019/8/1
+     */
+    ResultMap unifiedOrder(String vipGoodsId);
+
+    /**
+     * @param notifyStr: 微信异步通知消息字符串
+     * @return
+     * @Description: 订单支付异步通知
+     * @Author:
+     * @Date: 2019/8/1
+     */
+    String notify(String notifyStr) throws Exception;
+
+    /**
+     * @param orderNo:      订单编号
+     * @param amount:       实际支付金额
+     * @param refundReason: 退款原因
+     * @return
+     * @Description: 退款
+     * @Author: XCK
+     * @Date: 2019/8/6
+     */
+    ResultMap refund(String orderNo, double amount, String refundReason);
+
+    AppPayData unifiedOrder2(String vipGoodsId);
+
+    SysUser success(String vipGoodsId, String out_trade_no);
+
+    Transaction queryOrderByOutTradeNo(String out_trade_no);
+
+    // ============= 小程序支付相关接口 =============
+    
+    /**
+     * 小程序支付统一下单
+     * @param vipGoodsId VIP商品主键
+     * @param openid 用户openid
+     * @return 支付参数
+     */
+    Object miniprogramUnifiedOrder(String vipGoodsId, String openid);
+    
+    /**
+     * 小程序支付异步通知
+     * @param notifyStr 微信异步通知消息字符串
+     * @return 响应结果
+     */
+    String miniprogramNotify(String notifyStr);
+    
+    /**
+     * 小程序支付成功后处理
+     * @param vipGoodsId VIP商品主键
+     * @param out_trade_no 订单号
+     * @return 用户信息
+     */
+    SysUser miniprogramSuccess(String vipGoodsId, String out_trade_no);
+
+}

+ 860 - 0
src/main/java/com/dtb/portal/service/impl/SysUserServiceImpl.java

@@ -0,0 +1,860 @@
+package com.dtb.portal.service.impl;
+
+import com.dtb.portal.config.interceptor.RedisUtils;
+import com.dtb.portal.config.interceptor.TokenConfig;
+import com.dtb.portal.config.interceptor.TokenUtils;
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.controller.view.request.UserAddByNamePassRequest;
+import com.dtb.portal.controller.view.request.UserAddRequest;
+import com.dtb.portal.controller.view.request.UserRemoveNotCheckRequest;
+import com.dtb.portal.controller.view.request.UserRemoveRequest;
+import com.dtb.portal.controller.view.request.UserRequest;
+import com.dtb.portal.controller.view.request.UserUpdateRequest;
+import com.dtb.portal.entity.SysUser;
+import com.dtb.portal.entity.VipUserData;
+import com.dtb.portal.mapper.SysUserMapper;
+import com.alibaba.fastjson.JSONObject;
+import com.dtb.portal.util.DateUtils;
+import com.dtb.portal.util.MobileUtil;
+import com.dtb.portal.util.PrimaryIdUtils;
+import com.dtb.portal.util.SendSms;
+import com.dtb.portal.util.WxPhoneNumberUtils;
+import com.dtb.portal.util.*;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author fuzhiyou
+ * @date 2022-04-11 10:26
+ */
+
+@Slf4j
+@Service
+public class SysUserServiceImpl {
+
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+    @Autowired
+    private SysUserMapper sysUserMapper;
+    @Autowired
+    private AliyunSMSUtil aliyunSMSUtil;
+
+/*    @Autowired
+    private TokenConfig config;*/
+
+    private static long redisExpire = 7200L;
+
+    @Value("${wechat.miniprogram.app_id:}")
+    private String wxMiniProgramAppId;
+    @Value("${wechat.miniprogram.app_secret:}")
+    private String wxMiniProgramAppSecret;
+    
+    @Value("${wechat.app.app_id:}")
+    private String wxAppId;
+    @Value("${wechat.app.app_secret:}")
+    private String wxAppSecret;
+
+    public PageInfo<SysUser> page(PageQuery<UserRequest> pageQuery) {
+        PageHelper.startPage(pageQuery.getCurrentPage(), pageQuery.getPageSize());
+        List<SysUser> sysUsers = sysUserMapper.page(pageQuery.getQueryBean());
+        return new PageInfo<>(sysUsers);
+    }
+
+    public void enable(String id, String enable) {
+        SysUser sysUser = sysUserMapper.selectOne(id);
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+        sysUser.setEnable(Boolean.valueOf(enable));
+        sysUser.setUpdateTime(new Date());
+        sysUserMapper.updateEnable(sysUser);
+
+    }
+
+    public void updatePassword(String id, String password) {
+        SysUser sysUser = sysUserMapper.selectOne(id);
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+        sysUser.setUpdateTime(new Date());
+        sysUser.setPassword(password);
+        sysUserMapper.updatePassword(sysUser);
+
+    }
+
+    public SysUser login(String accountName, String password) {
+        //根据用户名密码查询
+        SysUser sysUser = sysUserMapper.selectByAccountName(accountName);
+        if (sysUser == null) {
+            throw new RuntimeException("用户不存在");
+        }
+        if (StringUtils.isBlank(password)) {
+            throw new RuntimeException("密码为空");
+        }
+        if (!StringUtils.equals(password, sysUser.getPassword())) {
+            throw new RuntimeException("密码输入不对");
+        }
+        //判断是不是VIP用户
+        idVipUser(sysUser);
+        String token = TokenUtils.createToken(sysUser.getId(), null, sysUser.getAccountName(),
+                sysUser.getAccountName(), sysUser.getPhone());
+        // 用户信息存储到redis,并设置有效期
+        redisTemplate.opsForValue().set(String.valueOf(sysUser.getId()), token, redisExpire, TimeUnit.SECONDS);
+        sysUser.setToken(token);
+        return sysUser;
+
+    }
+
+    public SysUser findByOpenId(String openId) {
+        return sysUserMapper.selectByOpenId(openId);
+    }
+
+    public SysUser update(UserUpdateRequest request) {
+        SysUser sysUser = sysUserMapper.selectOne(request.getId());
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+
+//        if (request.getNickname().length() > 6) {
+//            throw new RuntimeException("昵称不能超过6个字");
+//        }
+//        if (request.getAccountName().length() > 11) {
+//            throw new RuntimeException("用户名不能超过11个字");
+//        }
+
+        UserRequest userRequest = new UserRequest();
+        userRequest.setAccountName(request.getAccountName());
+        List<SysUser> sysUsers = sysUserMapper.page(userRequest);
+
+
+        if(sysUsers.size() == 0){
+            BeanUtils.copyProperties(request, sysUser);
+            sysUser.setUpdateTime(new Date());
+
+            sysUserMapper.update(sysUser);
+            return sysUser;
+        }else{
+            for (int i = 0; i <sysUsers.size() ; i++) {
+                SysUser user = sysUsers.get(i);
+                if (user.getId().equals(request.getId())){
+                    //        if (request.getAccountName().equals(sysUser.getAccountName())) {
+//            throw new RuntimeException("用户名重复");
+//        }
+
+                    BeanUtils.copyProperties(request, sysUser);
+                    sysUser.setUpdateTime(new Date());
+
+                    sysUserMapper.update(sysUser);
+                    return sysUser;
+                }
+            }
+            throw new RuntimeException("用户名重复");
+        }
+
+
+
+    }
+
+    public SysUser add(UserAddRequest request) {
+        SysUser sysUser = sysUserMapper.selectByAccountName(request.getAccountName());
+        if (Objects.nonNull(sysUser)) {
+            throw new RuntimeException("用户名已经存在");
+        }
+        if (request.getNickname().length() > 6) {
+            throw new RuntimeException("昵称不能超过6个字");
+        }
+        if (request.getAccountName().length() > 11) {
+            throw new RuntimeException("用户名不能超过11个字");
+        }
+
+
+
+//        VipUserData saveVipUserData = new VipUserData();
+//        saveVipUserData.setId(PrimaryIdUtils.getId().toString());
+//        saveVipUserData.setUserId(PrimaryIdUtils.getId().toString());
+//        saveVipUserData.setVipGoodsId(vipGoodsId);
+//        saveVipUserData.setVipStartDate(vipStartDate);
+//        saveVipUserData.setVipEndDate(vipEndDate);
+//        saveVipUserData.setCreateTime(LocalDateTime.now());
+//        saveVipUserData.setUpdateTime(LocalDateTime.now());
+//        vipGoodsService.saveVipUserData(saveVipUserData);
+
+
+
+        SysUser user = new SysUser();
+        BeanUtils.copyProperties(request, user);
+        user.setCreateTime(new Date());
+        user.setUpdateTime(new Date());
+        user.setId(PrimaryIdUtils.getId().toString());
+        user.setType("2");
+        user.setEnable(true);
+        sysUserMapper.save(user);
+
+
+        //根据用户名密码查询
+         sysUser = sysUserMapper.selectByAccountName(request.getAccountName());
+        String token = TokenUtils.createToken(sysUser.getId(), null, sysUser.getAccountName(),
+                sysUser.getAccountName(), sysUser.getPhone());
+        // 用户信息存储到redis,并设置有效期
+        redisTemplate.opsForValue().set(String.valueOf(sysUser.getId()), token, redisExpire, TimeUnit.SECONDS);
+        sysUser.setToken(token);
+
+
+        //VIP时长
+        String vipStartDate = "";
+        String vipEndDate = "";
+        String vipDate = "1";
+        vipStartDate = DateUtils.toStringYMD(new Date());
+
+        Date date = new Date();
+
+        vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+        //修改用户表,添加是VIP用户,VIP最后时间,剩余时长
+        Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
+        SysUser vipSysUser = sysUserMapper.selectOne(sysUser.getId());
+        vipSysUser.setIsVip(true);
+        vipSysUser.setVipResidueDate(vipResidueDate);
+        vipSysUser.setVipEndDate(vipEndDate);
+        vipSysUser.setUpdateTime(date);
+        sysUserMapper.update(vipSysUser);
+
+
+        sysUser.setIsVip(true);
+        sysUser.setVipResidueDate(vipResidueDate);
+        sysUser.setVipEndDate(vipEndDate);
+        sysUser.setUpdateTime(date);
+
+        return sysUser;
+    }
+    public SysUser addByNamePass(UserAddByNamePassRequest request) {
+        SysUser sysUser = sysUserMapper.selectByAccountName(request.getAccountName());
+        if (Objects.nonNull(sysUser)) {
+            throw new RuntimeException("用户名已经存在");
+        }
+        SysUser sysUserPhone = sysUserMapper.selectByPhone(request.getPhone());
+        if (!Objects.isNull(sysUserPhone)) {
+            throw new RuntimeException("手机号已存在");
+        }
+        if (request.getAccountName().length() > 11) {
+            throw new RuntimeException("用户名不能超过11个字");
+        }
+        if (!MobileUtil.checkPhone(request.getPhone())) {
+            throw new RuntimeException("手机号格式不正确");
+        }
+        //查询用户是否存在
+        if (!Objects.isNull(sysUserPhone)) {
+            throw new RuntimeException("手机号码已绑定");
+        }
+
+        String checkCodeRedis = redisTemplate.opsForValue().get(request.getCheckCode());
+        if (StringUtils.isBlank(checkCodeRedis)) {
+            throw new RuntimeException("验证码错误");
+        }
+
+        SysUser user = new SysUser();
+        BeanUtils.copyProperties(request, user);
+        user.setCreateTime(new Date());
+        user.setUpdateTime(new Date());
+        user.setId(PrimaryIdUtils.getId().toString());
+        user.setType("2");
+        user.setEnable(true);
+        sysUserMapper.save(user);
+
+        log.info("添加完毕:{}", user);
+
+        //根据用户名密码查询
+         sysUser = sysUserMapper.selectByAccountName(request.getAccountName());
+        String token = TokenUtils.createToken(sysUser.getId(), null, sysUser.getAccountName(),
+                sysUser.getAccountName(), sysUser.getPhone());
+        // 用户信息存储到redis,并设置有效期
+        redisTemplate.opsForValue().set(String.valueOf(sysUser.getId()), token, redisExpire, TimeUnit.SECONDS);
+        sysUser.setToken(token);
+
+
+        //VIP时长
+        String vipStartDate = "";
+        String vipEndDate = "";
+        String vipDate = "1";
+        vipStartDate = DateUtils.toStringYMD(new Date());
+
+        Date date = new Date();
+
+        vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+        //修改用户表,添加是VIP用户,VIP最后时间,剩余时长
+        Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
+        SysUser vipSysUser = sysUserMapper.selectOne(sysUser.getId());
+        vipSysUser.setIsVip(true);
+        vipSysUser.setVipResidueDate(vipResidueDate);
+        vipSysUser.setVipEndDate(vipEndDate);
+        vipSysUser.setUpdateTime(date);
+        sysUserMapper.update(vipSysUser);
+
+
+        sysUser.setIsVip(true);
+        sysUser.setVipResidueDate(vipResidueDate);
+        sysUser.setVipEndDate(vipEndDate);
+        sysUser.setUpdateTime(date);
+
+        return sysUser;
+    }
+
+    /**
+     * 发送短信验证码
+     *
+     * @param phone
+     */
+    public void sendSms(String phone) {
+        //校验手机号格式
+        if (!MobileUtil.checkPhone(phone)) {
+            throw new RuntimeException("手机号格式不正确");
+        }
+        String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000));
+        try {
+            aliyunSMSUtil.sendSms(phone, code);
+            //设置验证码2分钟有限
+            redisTemplate.opsForValue().set(code, code, 5, TimeUnit.MINUTES);
+        } catch (Exception e) {
+            throw new RuntimeException("获取验证码异常");
+        }
+
+    }
+
+    public SysUser forgotPassword(String phone) {
+        SysUser sysUser = sysUserMapper.selectByPhone(phone);
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+        String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000));
+        aliyunSMSUtil.sendSms(sysUser.getPhone(), code);
+        return sysUser;
+    }
+
+    public SysUser loginByOpenId(String openId) {
+        SysUser sysUser = sysUserMapper.selectByOpenId(openId);
+        //判断是不是VIP用户
+        idVipUser(sysUser);
+        String token = TokenUtils.createToken(sysUser.getId(), null, sysUser.getAccountName(),
+                sysUser.getAccountName(), sysUser.getPhone());
+        // 用户信息存储到redis,并设置有效期
+        redisTemplate.opsForValue().set(String.valueOf(sysUser.getId()), token, redisExpire, TimeUnit.SECONDS);
+        sysUser.setToken(token);
+        return sysUser;
+    }
+
+    public SysUser loginBySms(String phone, String checkCode, String openId) {
+
+        if (!MobileUtil.checkPhone(phone)) {
+            throw new RuntimeException("手机号格式不正确");
+        }
+        //查询用户是否存在
+        SysUser sysUser = sysUserMapper.selectByPhone(phone);
+        boolean isNewUser = false;
+        
+        if (Objects.isNull(sysUser)) {
+            // 用户不存在,自动注册新用户
+            isNewUser = true;
+            sysUser = new SysUser();
+            sysUser.setId(PrimaryIdUtils.getId().toString());
+            sysUser.setPhone(phone);
+            sysUser.setOpenId(openId);
+            sysUser.setType("2");
+            sysUser.setEnable(true);
+            sysUser.setCreateTime(new Date());
+            sysUser.setUpdateTime(new Date());
+            sysUser.setNewUser(true);
+            
+            // 设置基本信息默认值
+            sysUser.setAccountName("用户" + phone.substring(7)); // 使用手机号后4位作为用户名
+            sysUser.setNickname("用户" + phone.substring(7)); // 使用手机号后4位作为昵称
+            // sysUser.setPassword("123456"); // 默认密码
+            sysUser.setGender("1"); // 默认性别:1-男
+            sysUser.setLocation("未知"); // 默认地区
+            sysUser.setNation("中国"); // 默认国家
+            sysUser.setLogoPath("https://fun9527.com/static/newUser.jpg"); // 默认头像
+            
+            // 保存新用户
+            sysUserMapper.save(sysUser);
+            
+            // 设置VIP时长
+            String vipStartDate = DateUtils.toStringYMD(new Date());
+            String vipDate = "1";
+            String vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+            Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
+            
+            sysUser.setIsVip(true);
+            sysUser.setVipResidueDate(vipResidueDate);
+            sysUser.setVipEndDate(vipEndDate);
+            
+            // 更新用户VIP信息
+            SysUser vipSysUser = sysUserMapper.selectOne(sysUser.getId());
+            vipSysUser.setIsVip(true);
+            vipSysUser.setVipResidueDate(vipResidueDate);
+            vipSysUser.setVipEndDate(vipEndDate);
+            vipSysUser.setUpdateTime(new Date());
+            sysUserMapper.update(vipSysUser);
+            
+        } else {
+            // 老用户,更新openId
+            if (StringUtils.isNotBlank(openId)) {
+                sysUser.setOpenId(openId);
+                sysUser.setUpdateTime(new Date());
+                sysUserMapper.update(sysUser);
+            }
+            sysUser.setNewUser(false);
+        }
+        
+        String checkCodeRedis = redisTemplate.opsForValue().get(checkCode);
+        if (StringUtils.isBlank(checkCodeRedis)) {
+            throw new RuntimeException("验证码错误");
+        }
+
+        //判断是不是VIP用户
+        idVipUser(sysUser);
+        String token = TokenUtils.createToken(sysUser.getId(), null, sysUser.getAccountName(),
+                sysUser.getAccountName(), sysUser.getPhone());
+        // 用户信息存储到redis,并设置有效期
+        redisTemplate.opsForValue().set(String.valueOf(sysUser.getId()), token, redisExpire, TimeUnit.SECONDS);
+        sysUser.setToken(token);
+
+        //删除验证码
+        redisTemplate.delete(checkCodeRedis);
+        return sysUser;
+    }
+
+    /**
+     * 微信手机号登录
+     * @param code 前端传来的手机号授权码
+     * @return 用户信息
+     */
+    public SysUser loginByWxPhoneNumber(String phoneCode, String loginCode) {
+        try {
+            // 获取微信配置
+            String appId = getWxAppId();
+            String appSecret = getWxAppSecret();
+            
+            // 通过code获取手机号和openId
+            JSONObject wxResult = WxPhoneNumberUtils.getPhoneNumberAndOpenId(appId, appSecret, phoneCode, loginCode);
+            String phoneNumber = wxResult.getString("phoneNumber");
+            String wxOpenId = wxResult.getString("openId");
+            
+            if (StringUtils.isBlank(phoneNumber)) {
+                throw new RuntimeException("获取手机号失败");
+            }
+            
+            if (StringUtils.isBlank(wxOpenId)) {
+                throw new RuntimeException("获取openId失败");
+            }
+            
+            log.info("微信获取到手机号: {}, openId: {}", phoneNumber, wxOpenId);
+            
+            // 查询用户是否存在
+            SysUser sysUser = sysUserMapper.selectByPhone(phoneNumber);
+            boolean isNewUser = false;
+            
+            if (Objects.isNull(sysUser)) {
+                // 用户不存在,自动注册新用户
+                isNewUser = true;
+                sysUser = new SysUser();
+                sysUser.setId(PrimaryIdUtils.getId().toString());
+                sysUser.setPhone(phoneNumber);
+                sysUser.setOpenId(wxOpenId);
+                sysUser.setType("2");
+                sysUser.setEnable(true);
+                sysUser.setCreateTime(new Date());
+                sysUser.setUpdateTime(new Date());
+                sysUser.setNewUser(true);
+                
+                // 设置基本信息默认值
+                sysUser.setAccountName("用户" + phoneNumber.substring(7)); // 使用手机号后4位作为用户名
+                sysUser.setNickname("用户" + phoneNumber.substring(7)); // 使用手机号后4位作为昵称
+                sysUser.setGender("1"); // 默认性别:1-男
+                sysUser.setLocation("未知"); // 默认地区
+                sysUser.setNation("中国"); // 默认国家
+                sysUser.setLogoPath("https://fun9527.com/static/newUser.jpg"); // 默认头像
+                
+                // 保存新用户
+                sysUserMapper.save(sysUser);
+                
+                // 设置VIP时长
+                String vipStartDate = DateUtils.toStringYMD(new Date());
+                String vipDate = "1";
+                String vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+                Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
+                
+                sysUser.setIsVip(true);
+                sysUser.setVipResidueDate(vipResidueDate);
+                sysUser.setVipEndDate(vipEndDate);
+                
+                // 更新用户VIP信息
+                SysUser vipSysUser = sysUserMapper.selectOne(sysUser.getId());
+                vipSysUser.setIsVip(true);
+                vipSysUser.setVipResidueDate(vipResidueDate);
+                vipSysUser.setVipEndDate(vipEndDate);
+                vipSysUser.setUpdateTime(new Date());
+                sysUserMapper.update(vipSysUser);
+                
+            } else {
+                // 老用户,更新openId
+                // 更新用户的openId
+                sysUser.setOpenId(wxOpenId);
+                sysUser.setUpdateTime(new Date());
+                sysUserMapper.update(sysUser);
+                sysUser.setNewUser(false);
+            }
+            
+            //判断是不是VIP用户
+            idVipUser(sysUser);
+            String token = TokenUtils.createToken(sysUser.getId(), null, sysUser.getAccountName(),
+                    sysUser.getAccountName(), sysUser.getPhone());
+            // 用户信息存储到redis,并设置有效期
+            redisTemplate.opsForValue().set(String.valueOf(sysUser.getId()), token, redisExpire, TimeUnit.SECONDS);
+            sysUser.setToken(token);
+            
+            return sysUser;
+            
+        } catch (Exception e) {
+            log.error("微信手机号登录失败", e);
+            throw new RuntimeException("微信手机号登录失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取微信小程序appId
+     */
+    public String getWxAppId() {
+        return wxMiniProgramAppId;
+    }
+
+    /**
+     * 获取微信小程序appSecret
+     */
+    public String getWxAppSecret() {
+        return wxMiniProgramAppSecret;
+    }
+    
+    /**
+     * 获取APP微信配置的appId
+     */
+    public String getWxAppAppId() {
+        return wxAppId;
+    }
+
+    /**
+     * 获取APP微信配置的appSecret
+     */
+    public String getWxAppAppSecret() {
+        return wxAppSecret;
+    }
+    
+    /**
+     * APP微信登录
+     * @param code 微信登录code
+     * @param avatarUrl 头像URL
+     * @param nickName 昵称
+     * @return 用户信息
+     */
+    public SysUser loginByWxApp(String code, String avatarUrl, String nickName) {
+        try {
+            // 获取微信配置
+            String appId = getWxAppAppId();
+            String appSecret = getWxAppAppSecret();
+            
+            // 通过code获取openId
+            JSONObject wxResult = WxPhoneNumberUtils.getOpenIdByCode(appId, appSecret, code);
+            String wxOpenId = wxResult.getString("openid");
+            String unionId = wxResult.getString("unionid"); // 如果有unionId也保存
+            
+            if (StringUtils.isBlank(wxOpenId)) {
+                throw new RuntimeException("获取openId失败");
+            }
+            
+            log.info("APP微信登录获取到 openId: {}, unionId: {}", wxOpenId, unionId);
+            
+            // 先通过openId查询用户
+            SysUser sysUser = sysUserMapper.selectByOpenId(wxOpenId);
+            
+            if (Objects.isNull(sysUser)) {
+                // 用户不存在,返回null让前端跳转到注册页面
+                return null;
+            }
+            
+            // 用户存在,更新用户信息
+            if (StringUtils.isNotBlank(avatarUrl) || StringUtils.isNotBlank(nickName)) {
+                if (StringUtils.isNotBlank(avatarUrl)) {
+                    sysUser.setLogoPath(avatarUrl);
+                }
+                if (StringUtils.isNotBlank(nickName)) {
+                    sysUser.setNickname(nickName);
+                }
+                sysUser.setUpdateTime(new Date());
+                sysUserMapper.update(sysUser);
+            }
+            
+            //判断是不是VIP用户
+            idVipUser(sysUser);
+            String token = TokenUtils.createToken(sysUser.getId(), null, sysUser.getAccountName(),
+                    sysUser.getAccountName(), sysUser.getPhone());
+            // 用户信息存储到redis,并设置有效期
+            redisTemplate.opsForValue().set(String.valueOf(sysUser.getId()), token, redisExpire, TimeUnit.SECONDS);
+            sysUser.setToken(token);
+            
+            return sysUser;
+            
+        } catch (Exception e) {
+            log.error("APP微信登录失败", e);
+            throw new RuntimeException("APP微信登录失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 小程序微信登录
+     * @param code 微信登录code
+     * @param phoneCode 手机号授权code
+     * @return 用户信息
+     */
+    public SysUser loginByWxMini(String code, String phoneCode) {
+        try {
+            // 获取微信配置
+            String appId = getWxAppId();
+            String appSecret = getWxAppSecret();
+            
+            // 通过code获取手机号和openId
+            JSONObject wxResult = WxPhoneNumberUtils.getPhoneNumberAndOpenId(appId, appSecret, phoneCode, code);
+            String phoneNumber = wxResult.getString("phoneNumber");
+            String wxOpenId = wxResult.getString("openId");
+            String unionId = wxResult.getString("unionId"); // 如果有unionId也保存
+            
+            if (StringUtils.isBlank(phoneNumber)) {
+                throw new RuntimeException("获取手机号失败");
+            }
+            
+            if (StringUtils.isBlank(wxOpenId)) {
+                throw new RuntimeException("获取openId失败");
+            }
+            
+            log.info("小程序微信登录获取到 手机号: {}, openId: {}, unionId: {}", phoneNumber, wxOpenId, unionId);
+            
+            // 先通过手机号查询用户
+            SysUser sysUser = sysUserMapper.selectByPhone(phoneNumber);
+            boolean isNewUser = false;
+            
+            if (Objects.isNull(sysUser)) {
+                // 用户不存在,自动注册
+                isNewUser = true;
+                sysUser = new SysUser();
+                sysUser.setId(PrimaryIdUtils.getId().toString());
+                sysUser.setPhone(phoneNumber);
+                sysUser.setOpenId(wxOpenId);
+                sysUser.setType("2");
+                sysUser.setEnable(true);
+                sysUser.setCreateTime(new Date());
+                sysUser.setUpdateTime(new Date());
+                sysUser.setNewUser(true);
+                
+                // 设置基本信息
+                sysUser.setAccountName("用户" + phoneNumber.substring(7));
+                sysUser.setNickname("用户" + phoneNumber.substring(7));
+                sysUser.setGender("1");
+                sysUser.setLocation("未知");
+                sysUser.setNation("中国");
+                sysUser.setLogoPath("https://fun9527.com/static/newUser.jpg");
+                
+                // 保存新用户
+                sysUserMapper.save(sysUser);
+                
+                // 设置新用户VIP
+                String vipStartDate = DateUtils.toStringYMD(new Date());
+                String vipDate = "1";
+                String vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+                Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
+                
+                sysUser.setIsVip(true);
+                sysUser.setVipResidueDate(vipResidueDate);
+                sysUser.setVipEndDate(vipEndDate);
+                
+                // 更新VIP信息
+                SysUser vipSysUser = sysUserMapper.selectOne(sysUser.getId());
+                vipSysUser.setIsVip(true);
+                vipSysUser.setVipResidueDate(vipResidueDate);
+                vipSysUser.setVipEndDate(vipEndDate);
+                vipSysUser.setUpdateTime(new Date());
+                sysUserMapper.update(vipSysUser);
+                
+            } else {
+                // 用户存在,更新openId
+                sysUser.setOpenId(wxOpenId);
+                sysUser.setUpdateTime(new Date());
+                sysUserMapper.update(sysUser);
+                sysUser.setNewUser(false);
+            }
+            
+            //判断是不是VIP用户
+            idVipUser(sysUser);
+            String token = TokenUtils.createToken(sysUser.getId(), null, sysUser.getAccountName(),
+                    sysUser.getAccountName(), sysUser.getPhone());
+            // 用户信息存储到redis,并设置有效期
+            redisTemplate.opsForValue().set(String.valueOf(sysUser.getId()), token, redisExpire, TimeUnit.SECONDS);
+            sysUser.setToken(token);
+            
+            return sysUser;
+            
+        } catch (Exception e) {
+            log.error("小程序微信登录失败", e);
+            throw new RuntimeException("小程序微信登录失败: " + e.getMessage());
+        }
+    }
+
+    public SysUser bindPhone(String id,String phone, String checkCode) {
+        SysUser sysUser = sysUserMapper.selectOne(id);
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+        if (!MobileUtil.checkPhone(phone)) {
+            throw new RuntimeException("手机号格式不正确");
+        }
+        //查询用户是否存在
+        SysUser sysUserPhone = sysUserMapper.selectByPhone(phone);
+        if (!Objects.isNull(sysUserPhone)) {
+            throw new RuntimeException("手机号码已绑定");
+        }
+
+
+        String checkCodeRedis = redisTemplate.opsForValue().get(checkCode);
+        if (StringUtils.isBlank(checkCodeRedis)) {
+            throw new RuntimeException("验证码错误");
+        }
+        Date date = new Date();
+        sysUser.setPhone(phone);
+        sysUser.setUpdateTime(date);
+        sysUserMapper.update(sysUser);
+
+        //删除验证码
+        redisTemplate.delete(checkCodeRedis);
+        return sysUser;
+    }
+    public SysUser forgotPasswordByPhone(String phone, String password, String checkCode) {
+        SysUser sysUser = sysUserMapper.selectByPhone(phone);
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+
+        String checkCodeRedis = redisTemplate.opsForValue().get(checkCode);
+        if (StringUtils.isBlank(checkCodeRedis)) {
+            throw new RuntimeException("验证码错误");
+        }
+
+        Date date = new Date();
+        sysUser.setPassword(password);
+        sysUser.setUpdateTime(date);
+        sysUserMapper.update(sysUser);
+
+        //删除验证码
+        redisTemplate.delete(checkCodeRedis);
+        return sysUser;
+    }
+
+    /**
+     * 更换微信 openId
+     * @param id 用户id
+     * @param newOpenId 新的openId
+     * @return 更新后的SysUser
+     */
+    public SysUser changeOpenId(String id, String newOpenId) {
+        if (StringUtils.isBlank(id) || StringUtils.isBlank(newOpenId)) {
+            throw new RuntimeException("用户id和新的openId不能为空");
+        }
+        SysUser exist = sysUserMapper.selectByOpenId(newOpenId);
+        if (exist != null) {
+            throw new RuntimeException("该微信已绑定");
+        }
+        SysUser sysUser = sysUserMapper.selectOne(id);
+        if (sysUser == null) {
+            throw new RuntimeException("用户不存在");
+        }
+        sysUser.setOpenId(newOpenId);
+        sysUser.setUpdateTime(new Date());
+        sysUserMapper.update(sysUser);
+        return sysUser;
+    }
+
+    /**
+     * 判断是否是VIP用户
+     *
+     * @param sysUser
+     */
+    private void idVipUser(SysUser sysUser) {
+        if (BooleanUtils.isTrue(sysUser.getIsVip())) {
+            //计算剩余VIP天数
+            Date date = new Date();
+            String vipEndDateStr = sysUser.getVipEndDate();
+            Date vipEndDate = DateUtils.toDateYMDHMS(vipEndDateStr);
+            if (vipEndDate.compareTo(date) == 1 || vipEndDate.compareTo(date) == 0) {
+                //有剩余天数
+                Long vipResidueDate = DateUtils.getDifferenceTime(date, vipEndDate);
+                sysUser.setVipResidueDate(vipResidueDate);
+            }
+        } else {
+            sysUser.setIsVip(false);
+        }
+    }
+
+    /**
+     * 注销用户
+     *
+     * @param user
+     */
+    public SysUser delete(UserRemoveRequest user) {
+        SysUser sysUser = sysUserMapper.selectOne(user.getId());
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+        String checkCodeRedis = redisTemplate.opsForValue().get(user.getCheckCode());
+        if (StringUtils.isBlank(checkCodeRedis)) {
+            throw new RuntimeException("验证码错误");
+        }
+        sysUserMapper.delete(user.getId());
+        return  sysUser;
+    }
+    /**
+     * 注销用户不用验证
+     *
+     * @param user
+     */
+    public SysUser deleteNotCheck(UserRemoveNotCheckRequest user) {
+        SysUser sysUser = sysUserMapper.selectOne(user.getId());
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+        sysUserMapper.delete(user.getId());
+        return  sysUser;
+    }
+    /**
+     * 获取这个用户最新信息
+     *
+     * @param id
+     */
+    public SysUser get(String id) {
+        SysUser sysUser = sysUserMapper.selectOne(id);
+        if (Objects.isNull(sysUser)) {
+            throw new RuntimeException("用户不存在");
+        }
+        //判断是不是VIP用户
+        idVipUser(sysUser);
+        return  sysUser;
+    }
+}

+ 75 - 0
src/main/java/com/dtb/portal/service/impl/VideoServiceImpl.java

@@ -0,0 +1,75 @@
+package com.dtb.portal.service.impl;
+
+import com.dtb.portal.config.interceptor.RedisUtils;
+import com.dtb.portal.config.interceptor.TokenUtils;
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.controller.view.request.*;
+import com.dtb.portal.entity.SysUser;
+import com.dtb.portal.entity.Video;
+import com.dtb.portal.mapper.SysUserMapper;
+import com.dtb.portal.mapper.VideoMapper;
+import com.dtb.portal.util.*;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author fuzhiyou
+ * @date 2022-04-11 10:26
+ */
+
+@Slf4j
+@Service
+public class VideoServiceImpl {
+
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+    @Autowired
+    private VideoMapper videoMapper;
+
+    @Autowired
+    private AliyunOSSUtil aliyunOSSUtil;
+
+/*    @Autowired
+    private TokenConfig config;*/
+
+    private static long redisExpire = 7200L;
+
+    public PageInfo<Video> page(PageQuery<VideoRequest> pageQuery) {
+        PageHelper.startPage(pageQuery.getCurrentPage(), pageQuery.getPageSize());
+        String keyWord = pageQuery.getQueryBean().getKeyWord();
+        List<Video> sysUsers = videoMapper.page(keyWord);
+        return new PageInfo<>(sysUsers);
+    }
+
+    public void update(Video video) {
+        videoMapper.updateEnable(video);
+    }
+
+    public void delete(String id){
+        Video video = videoMapper.selectOne(id);
+        if (Objects.isNull(video)) {
+            throw new RuntimeException("记录不存在");
+        }
+        if(video.getVideoImg() !=null&&video.getVideoImg().length()!=0){
+            aliyunOSSUtil.deleteBlog(video.getVideoImg());
+        }
+        if(video.getVideoPath() !=null&&video.getVideoPath().length()!=0){
+            aliyunOSSUtil.deleteBlog(video.getVideoPath());
+        }
+        videoMapper.delete(id);
+    }
+
+}

+ 82 - 0
src/main/java/com/dtb/portal/service/impl/VideoUserServiceImpl.java

@@ -0,0 +1,82 @@
+package com.dtb.portal.service.impl;
+
+import com.dtb.portal.entity.VideoUser;
+import com.dtb.portal.controller.view.request.VideoUserRequest;
+import com.dtb.portal.controller.view.request.VideoUserUpdateRequest;
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.mapper.VideoUserMapper;
+import com.dtb.portal.service.VideoUserService;
+import com.dtb.portal.util.AliyunOSSUtil;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+@Service
+public class VideoUserServiceImpl implements VideoUserService {
+    @Autowired
+    private VideoUserMapper videoUserMapper;
+
+    @Autowired
+    private AliyunOSSUtil aliyunOSSUtil;
+
+    @Override
+    public VideoUser add(VideoUserRequest request) {
+        VideoUser videoUser = new VideoUser();
+        BeanUtils.copyProperties(request, videoUser);
+        videoUser.setId(UUID.randomUUID().toString().replaceAll("-", ""));
+        videoUser.setCreateTime(new Date());
+        videoUser.setUpdateTime(new Date());
+        videoUserMapper.insert(videoUser);
+        return videoUser;
+    }
+
+    @Override
+    public VideoUser update(VideoUserUpdateRequest request) {
+        VideoUser videoUser = new VideoUser();
+        BeanUtils.copyProperties(request, videoUser);
+        videoUser.setUpdateTime(new Date());
+        videoUserMapper.update(videoUser);
+        return videoUserMapper.selectById(videoUser.getId());
+    }
+
+    @Override
+    public void delete(String id) {
+        VideoUser videoUser = videoUserMapper.selectById(id);
+        if (videoUser != null) {
+            if (videoUser.getVideoImg() != null && videoUser.getVideoImg().length() != 0) {
+                aliyunOSSUtil.deleteBlog(videoUser.getVideoImg());
+            }
+            if (videoUser.getVideoPath() != null && videoUser.getVideoPath().length() != 0) {
+                aliyunOSSUtil.deleteBlog(videoUser.getVideoPath());
+            }
+        }
+        videoUserMapper.deleteById(id);
+    }
+
+    @Override
+    public VideoUser get(String id) {
+        return videoUserMapper.selectById(id);
+    }
+
+    @Override
+    public PageInfo<VideoUser> page(PageQuery<VideoUserRequest> pageQuery) {
+        PageHelper.startPage(pageQuery.getCurrentPage(), pageQuery.getPageSize());
+        VideoUserRequest query = pageQuery.getQueryBean();
+        List<VideoUser> list = videoUserMapper.selectList(
+                query != null ? query.getName() : null,
+                query != null ? query.getContent() : null
+        );
+        return new PageInfo<>(list);
+    }
+
+    @Override
+    public java.util.List<VideoUser> list(String name, String content) {
+        return videoUserMapper.selectList(name, content);
+    }
+} 

+ 119 - 0
src/main/java/com/dtb/portal/service/impl/VipGoodsServiceImpl.java

@@ -0,0 +1,119 @@
+package com.dtb.portal.service.impl;
+
+import com.dtb.portal.controller.view.pageable.PageQuery;
+import com.dtb.portal.controller.view.request.VipGoodsRequest;
+import com.dtb.portal.entity.VipGoodsData;
+import com.dtb.portal.entity.VipOrderData;
+import com.dtb.portal.entity.VipUserData;
+import com.dtb.portal.mapper.VipGoodsMapper;
+import com.dtb.portal.service.VipGoodsService;
+import com.dtb.portal.util.PrimaryIdUtils;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+
+
+@Slf4j
+@Service
+@Transactional(value = "masterTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
+public class VipGoodsServiceImpl implements VipGoodsService {
+
+    @Autowired
+    private VipGoodsMapper vipGoodsMapper;
+
+    @Override
+    public VipGoodsData add(VipGoodsRequest request) {
+        //查询商品名称是否存在
+        VipGoodsData vipGoodsData = vipGoodsMapper.selectByName(request.getName(), null);
+        if (Objects.nonNull(vipGoodsData)) {
+            throw new RuntimeException("商品名称已经存在");
+        }
+        VipGoodsData data = new VipGoodsData();
+        BeanUtils.copyProperties(request, data);
+        data.setId(PrimaryIdUtils.getId().toString());
+        data.setCreateTime(LocalDateTime.now());
+        data.setUpdateTime(LocalDateTime.now());
+        log.info("添加的VIP商品是:{}", data);
+        vipGoodsMapper.insert(data);
+        return data;
+    }
+
+    @Override
+    public VipGoodsData update(VipGoodsRequest request) {
+        //查询商品名称是否存在
+        VipGoodsData vipGoodsData = vipGoodsMapper.selectByName(request.getName(), request.getId());
+        if (Objects.nonNull(vipGoodsData)) {
+            throw new RuntimeException("商品名称已经存在");
+        }
+        VipGoodsData data = new VipGoodsData();
+        BeanUtils.copyProperties(request, data);
+        data.setUpdateTime(LocalDateTime.now());
+        vipGoodsMapper.update(data);
+        log.info("更新VIP商品是:{}", data);
+        return data;
+    }
+
+    @Override
+    public VipGoodsData delete(String id) {
+        //查询商品是否存在
+        VipGoodsData data = vipGoodsMapper.selectById(id);
+        if (Objects.isNull(data)) {
+            throw new RuntimeException("商品信息不存在");
+        }
+        vipGoodsMapper.delete(id);
+        log.info("删除的商品是:{}", data);
+        return data;
+    }
+
+    @Override
+    public PageInfo<VipGoodsData> page(PageQuery<VipGoodsRequest> pageQuery) {
+        PageHelper.startPage(pageQuery.getCurrentPage(), pageQuery.getPageSize());
+        VipGoodsRequest queryBean = pageQuery.getQueryBean();
+        List<VipGoodsData> dataList = vipGoodsMapper.selectPage(queryBean);
+        return new PageInfo<>(dataList);
+    }
+
+    @Override
+    public VipUserData selectByUserId(String userId, String vipGoodsId) {
+        return vipGoodsMapper.selectByUserId(userId,vipGoodsId);
+    }
+
+    @Override
+    public VipGoodsData getById(String vipGoodsId) {
+        return vipGoodsMapper.selectById(vipGoodsId);
+    }
+
+    @Override
+    public void saveVipUserData(VipUserData saveVipUserData) {
+        vipGoodsMapper.insertVipUser(saveVipUserData);
+    }
+
+    @Override
+    public void deleteVipUser(String id) {
+        vipGoodsMapper.deleteVipUser(id);
+    }
+
+    @Override
+    public void insertVipOrderData(VipOrderData data) {
+        vipGoodsMapper.insertVipOrderData(data);
+    }
+
+    @Override
+    public VipOrderData getOrderByOutTradeNo(String outTradeNo) {
+        return vipGoodsMapper.selectByOutTradeNo(outTradeNo);
+    }
+
+    @Override
+    public void updateVipOrderData(VipOrderData data) {
+        vipGoodsMapper.updateVipOrderData(data);
+    }
+}

+ 925 - 0
src/main/java/com/dtb/portal/service/impl/WxPayServiceImpl.java

@@ -0,0 +1,925 @@
+package com.dtb.portal.service.impl;
+
+import com.dtb.portal.config.WxPayAppConfig;
+import com.dtb.portal.config.WxPayMiniprogramConfig;
+import com.dtb.portal.config.interceptor.TokenUtils;
+import com.dtb.portal.controller.view.response.ResultMap;
+import com.dtb.portal.entity.AppPayData;
+import com.dtb.portal.entity.MiniprogramPayData;
+import com.dtb.portal.entity.VipGoodsData;
+import com.dtb.portal.entity.VipOrderData;
+import com.dtb.portal.entity.VipUserData;
+import com.dtb.portal.entity.SysUser;
+import com.dtb.portal.mapper.SysUserMapper;
+import com.dtb.portal.service.VipGoodsService;
+import com.dtb.portal.service.WxPayService;
+import com.dtb.portal.util.DateUtils;
+import com.dtb.portal.util.PrimaryIdUtils;
+import com.github.wxpay.sdk.WXPay;
+import com.github.wxpay.sdk.WXPayConstants;
+import com.github.wxpay.sdk.WXPayUtil;
+import com.wechat.pay.java.core.Config;
+import com.wechat.pay.java.core.RSAAutoCertificateConfig;
+
+import com.wechat.pay.java.core.cipher.SignatureResult;
+import com.wechat.pay.java.service.payments.app.AppService;
+import com.wechat.pay.java.service.payments.app.AppServiceExtension;
+import com.wechat.pay.java.service.payments.app.model.*;
+import com.wechat.pay.java.service.payments.jsapi.JsapiService;
+import com.wechat.pay.java.service.payments.jsapi.model.*;
+import com.wechat.pay.java.service.payments.jsapi.model.Payer;
+import com.wechat.pay.java.service.payments.model.Transaction;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.*;
+
+import static java.util.Objects.requireNonNull;
+
+@Service
+public class WxPayServiceImpl implements WxPayService {
+    private final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);
+
+
+    @Value("${wxpay.app.v3-key}")
+    private String v3Key;
+    @Autowired
+    private SysUserMapper sysUserMapper;
+/*    private HttpClient httpClient;
+
+
+    private WxPayServiceImpl(HttpClient httpClient) {
+        this.httpClient = httpClient;
+    }*/
+
+    @Autowired
+    private WxPayAppConfig wxPayAppConfig;
+    @Autowired
+    private WxPayMiniprogramConfig wxPayMiniprogramConfig;
+    @Autowired
+    private VipGoodsService vipGoodsService;
+
+    /**
+     * @param vipGoodsId : 订单编号
+     * @return
+     */
+    @Override
+    public ResultMap unifiedOrder(String vipGoodsId) {
+        Map<String, String> returnMap = new HashMap<>();
+        Map<String, String> responseMap = new HashMap<>();
+        Map<String, String> requestMap = new HashMap<>();
+        String orderNo = "";
+        try {
+
+            VipGoodsData vipGoodsData = vipGoodsService.getById(vipGoodsId);
+            if (Objects.isNull(vipGoodsData)) {
+                throw new RuntimeException("商品不存在");
+            }
+
+            String vipMoney = vipGoodsData.getVipMoney();
+            double amount = Double.parseDouble(vipMoney);
+            orderNo = PrimaryIdUtils.getId().toString();
+            WXPay wxpay = new WXPay(wxPayAppConfig);
+            requestMap.put("body", vipGoodsData.getName());                                     // 商品描述
+            requestMap.put("out_trade_no", orderNo);                          // 商户订单号
+            requestMap.put("total_fee", String.valueOf((int) (amount * 100)));   // 总金额
+            //requestMap.put("spbill_create_ip", HttpContextUtils.getIpAddr()); // 终端IP
+            requestMap.put("trade_type", "APP");                              // App支付类型
+            requestMap.put("notify_url", wxPayAppConfig.getPayNotifyUrl());   // 接收微信支付异步通知回调地址
+            Map<String, String> resultMap = wxpay.unifiedOrder(requestMap);
+            logger.info("下单返回的信息是:{}", resultMap);
+            //获取返回码
+            String returnCode = resultMap.get("return_code");
+            //String returnMsg = resultMap.get("return_msg");
+            //若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
+            if ("SUCCESS".equals(returnCode)) {
+                String resultCode = resultMap.get("result_code");
+                String errCodeDes = resultMap.get("err_code_des");
+                if ("SUCCESS".equals(resultCode)) {
+                    responseMap = resultMap;
+                }
+            }
+            if (responseMap.isEmpty()) {
+                return ResultMap.error("获取预支付交易会话标识失败");
+            }
+            // 3、签名生成算法
+            Long time = System.currentTimeMillis() / 1000;
+            String timestamp = time.toString();
+            returnMap.put("appid", wxPayAppConfig.getAppID());
+            returnMap.put("partnerid", wxPayAppConfig.getMchID());
+            returnMap.put("prepayid", responseMap.get("prepay_id"));
+            returnMap.put("noncestr", responseMap.get("nonce_str"));
+            returnMap.put("timestamp", timestamp);
+            returnMap.put("package", "Sign=WXPay");
+            returnMap.put("sign", responseMap.get("sign"));
+            returnMap.put("out_trade_no", orderNo);
+            String signMD5 = WXPayUtil.generateSignature(returnMap, wxPayAppConfig.getKey(), WXPayConstants.SignType.MD5);
+            String signSHA256 = WXPayUtil.generateSignature(returnMap, wxPayAppConfig.getKey(), WXPayConstants.SignType.HMACSHA256);
+            returnMap.put("signMD5", signMD5);
+            returnMap.put("signSHA256", signSHA256);
+            returnMap.put("base64SignMD5", Base64.getEncoder().encodeToString(signMD5.getBytes()));
+            returnMap.put("base64SignSHA256", Base64.getEncoder().encodeToString(signSHA256.getBytes()));
+            returnMap.put("key", wxPayAppConfig.getKey());
+            logger.info("返回的信息是:{}", returnMap);
+            return ResultMap.ok().put("data", returnMap);
+        } catch (Exception e) {
+            logger.error("订单号:{},错误信息:{}", orderNo, e.getMessage());
+            return ResultMap.error("微信支付统一下单失败");
+        } finally {
+            //生成订单在数据库
+            VipOrderData data = new VipOrderData();
+            data.setId(PrimaryIdUtils.getId().toString());
+            data.setOutTradeNo(orderNo);
+            data.setStatus("1");
+            data.setVipGoodsId(vipGoodsId);
+            data.setCreateTime(LocalDateTime.now());
+            data.setUpdateTime(LocalDateTime.now());
+            vipGoodsService.insertVipOrderData(data);
+
+        }
+    }
+
+    @Override
+    public String notify(String notifyStr) {
+        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
+        try {
+// 转换成map
+            Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
+            WXPay wxpayApp = new WXPay(wxPayAppConfig);
+            if (wxpayApp.isPayResultNotifySignatureValid(resultMap)) {
+                String returnCode = resultMap.get("return_code");  //状态
+                String outTradeNo = resultMap.get("out_trade_no");//商户订单号
+                String transactionId = resultMap.get("transaction_id");
+                if (returnCode.equals("SUCCESS")) {
+                    if (StringUtils.isNotBlank(outTradeNo)) {
+/**
+ * 注意!!!
+ * 请根据业务流程,修改数据库订单支付状态,和其他数据的相应状态
+ *
+ */
+                        logger.info("微信手机支付回调成功,订单号:{}", outTradeNo);
+                        xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return xmlBack;
+    }
+
+    @Override
+    public ResultMap refund(String orderNo, double amount, String refundReason) {
+
+        if (StringUtils.isBlank(orderNo)) {
+            return ResultMap.error("订单编号不能为空");
+        }
+        if (amount <= 0) {
+            return ResultMap.error("退款金额必须大于0");
+        }
+
+        Map<String, String> responseMap = new HashMap<>();
+        Map<String, String> requestMap = new HashMap<>();
+        WXPay wxpay = new WXPay(wxPayAppConfig);
+        requestMap.put("out_trade_no", orderNo);
+        requestMap.put("out_refund_no", UUID.randomUUID().toString().replaceAll("-", ""));
+        requestMap.put("total_fee", "订单支付时的总金额,需要从数据库查");
+        requestMap.put("refund_fee", String.valueOf((int) (amount * 100)));//所需退款金额
+        requestMap.put("refund_desc", refundReason);
+        try {
+            responseMap = wxpay.refund(requestMap);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        String return_code = responseMap.get("return_code");   //返回状态码
+        String return_msg = responseMap.get("return_msg");     //返回信息
+        if ("SUCCESS".equals(return_code)) {
+            String result_code = responseMap.get("result_code");       //业务结果
+            String err_code_des = responseMap.get("err_code_des");     //错误代码描述
+            if ("SUCCESS".equals(result_code)) {
+//表示退款申请接受成功,结果通过退款查询接口查询
+//修改用户订单状态为退款申请中或已退款。退款异步通知根据需求,可选
+                return ResultMap.ok("退款申请成功");
+            } else {
+                logger.info("订单号:{}错误信息:{}", orderNo, err_code_des);
+                return ResultMap.error(err_code_des);
+            }
+        } else {
+            logger.info("订单号:{}错误信息:{}", orderNo, return_msg);
+            return ResultMap.error(return_msg);
+        }
+    }
+
+
+    /**
+     * @param vipGoodsId 商品主键
+     * @return
+     */
+    @Override
+    public AppPayData unifiedOrder2(String vipGoodsId) {
+        logger.info("微信APP支付基础信息:{}", wxPayAppConfig);
+        //查询商品是否存在,存在则下单,不存在则不下单
+        VipGoodsData data = vipGoodsService.getById(vipGoodsId);
+        if (Objects.isNull(data)) {
+            throw new RuntimeException("VIP商品不存在");
+        }
+        // 使用自动更新平台证书的RSA配置
+        // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
+        Config config =
+                new RSAAutoCertificateConfig.Builder()
+                        .merchantId(wxPayAppConfig.getMchID())
+                        .privateKeyFromPath(wxPayAppConfig.getCertPath())
+                        .merchantSerialNumber(wxPayAppConfig.getMerchantSerialNumber())
+                        //.apiV3Key(wxPayAppConfig.getKey())
+                        .apiV3Key(v3Key)
+                        .build();
+
+        // 构建service
+        AppService service = new AppService.Builder().config(config).build();
+        String orderNo = PrimaryIdUtils.getId().toString();
+        com.wechat.pay.java.service.payments.app.model.PrepayRequest request = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
+        com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
+        //金额计算 ,乘以100
+        String money = data.getVipMoney();
+
+        //int orderAmount = Integer.parseInt(money) * 100;
+        int orderAmount = (int) (Double.parseDouble(money) * 100);
+
+        amount.setTotal(orderAmount);
+        request.setAmount(amount);
+        request.setAppid(wxPayAppConfig.getAppID());
+        request.setMchid(wxPayAppConfig.getMchID());
+        request.setDescription(data.getName());
+        request.setNotifyUrl(wxPayAppConfig.getPayNotifyUrl());
+        request.setOutTradeNo(orderNo);
+        // 调用下单方法,得到应答
+        com.wechat.pay.java.service.payments.app.model.PrepayResponse response = service.prepay(request);
+        logger.info("调用微信app下单返回的信息是:{}", response);
+        String prepayId = response.getPrepayId();
+        AppPayData appPayData = new AppPayData();
+        appPayData.setAppid(wxPayAppConfig.getAppID());
+        appPayData.setPrepayid(prepayId);
+        appPayData.setPartnerid(wxPayAppConfig.getMchID());
+        String noncestr = UUID.randomUUID().toString().replaceAll("-", "");
+        appPayData.setNoncestr(noncestr);
+        //appPayData.setTimestamp(String.valueOf(System.currentTimeMillis()));
+
+        //生成签名
+        Map<String, String> returnMap = new HashMap<>();
+        Long time = System.currentTimeMillis() / 1000;
+        String timestamp = time.toString();
+        returnMap.put("appid", wxPayAppConfig.getAppID());
+        returnMap.put("timestamp", timestamp);
+        returnMap.put("noncestr", noncestr);
+
+        returnMap.put("prepayid", prepayId);
+        //returnMap.put("package", "Sign=WXPay");
+        //returnMap.put("package", "prepay_id="+prepayId);
+
+        AppServiceExtension appServiceExtension = new AppServiceExtension.Builder().config(config).build();
+
+        com.wechat.pay.java.service.payments.app.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
+
+        prepayRequest.setAmount(amount);
+        prepayRequest.setAppid(wxPayAppConfig.getAppID());
+        prepayRequest.setMchid(wxPayAppConfig.getMchID());
+        prepayRequest.setDescription(data.getName());
+        prepayRequest.setNotifyUrl(wxPayAppConfig.getPayNotifyUrl());
+        prepayRequest.setOutTradeNo(orderNo);
+
+        com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse = appServiceExtension.prepayWithRequestPayment(prepayRequest);
+        appPayData.setPrepayWithRequestPaymentResponse(prepayWithRequestPaymentResponse);
+
+
+        SignatureResult signatureResult = config.createSigner().sign(wxPayAppConfig.getAppID() + "\n" + timestamp + "\n" + noncestr + "\n" + "prepay_id=" + prepayId + "\n");
+        String sign = signatureResult.getSign();
+        logger.info("签名值是:{}", sign);
+        appPayData.setSign(sign);
+        appPayData.setBase64Sign(Base64.getEncoder().encodeToString(sign.getBytes()));
+        try {
+            String signMD5 = WXPayUtil.generateSignature(returnMap, v3Key, WXPayConstants.SignType.MD5);
+            String signSHA256 = WXPayUtil.generateSignature(returnMap, v3Key, WXPayConstants.SignType.HMACSHA256);
+            //生成微信签名
+            appPayData.setSignMD5(signMD5);
+            appPayData.setBase64SignMD5(Base64.getEncoder().encodeToString(signMD5.getBytes()));
+            appPayData.setSignSHA256(signSHA256);
+            appPayData.setBase64SignSHA256(Base64.getEncoder().encodeToString(signSHA256.getBytes()));
+            appPayData.setTimestamp(timestamp);
+        } catch (Exception e) {
+            logger.error("生成微信签名失败:", e);
+            throw new RuntimeException("生成微信签名失败");
+
+        } finally {
+
+        }
+
+        return appPayData;
+    }
+
+
+    /**
+     * APP支付成功后调用,主要计算VIP时长
+     *
+     * @param vipGoodsId   商品主键
+     * @param out_trade_no
+     * @return
+     */
+    @Override
+    public SysUser success(String vipGoodsId, String out_trade_no) {
+
+        //查询商品是否存在,存在则下单,不存在则不下单
+        VipGoodsData data = vipGoodsService.getById(vipGoodsId);
+        if (Objects.isNull(data)) {
+            throw new RuntimeException("VIP商品不存在");
+        }
+
+        VipOrderData vipOrderData = vipGoodsService.getOrderByOutTradeNo(out_trade_no);
+        if (Objects.isNull(vipOrderData)) {
+            throw new RuntimeException("订单不存在");
+        }
+        //查询订单是否支付成功,支付成功:生成VIP时长,失败异常
+     /*   QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
+        queryOrderByOutTradeNoRequest.setOutTradeNo(out_trade_no);
+        queryOrderByOutTradeNoRequest.setMchid(wxPayAppConfig.getMchID());
+        Transaction transaction = queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);*/
+        Transaction transaction = queryOrderByOutTradeNo(out_trade_no);
+
+        Transaction.TradeStateEnum tradeState = transaction.getTradeState();
+        //logger.info("支付返回的信息是:{}", transaction);
+        //logger.info("根据订单号查询是否支付成功:{}", tradeState.name());
+
+
+        if ("SUCCESS".equals(tradeState.name())) {
+            logger.info("支付成功:{}", transaction);
+            //查询用户是否已经开通过,开通过则续费,没有开通过怎插新数据
+            String userId = TokenUtils.getUserId();
+            VipUserData vipUserData = vipGoodsService.selectByUserId(userId, vipGoodsId);
+            //VIP时长
+            String vipStartDate = "";
+            String vipEndDate = "";
+            String vipDate = data.getVipDate();
+            if (Objects.isNull(vipUserData)) {
+                //没有开通过,新开通,开始时间=当天时间,结束时间=会员时长+当前时间
+                vipStartDate = DateUtils.toStringYMD(new Date());
+                vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+
+            } else {
+                //开通过,查看有没有过期,过期:新开通,不过期,续费
+                String vipEndDateStr = vipUserData.getVipEndDate();
+                Date vipEndDate1 = DateUtils.toDateYMDHMS(vipEndDateStr);
+                Date todayDate = DateUtils.toDateYMDHMS(DateUtils.toStringYMD(new Date()));
+                int compareTo = todayDate.compareTo(vipEndDate1);
+                //0:到当天 1:过期 -1:不过期
+                if (compareTo == 0 || compareTo == -1) {
+                    //不过期,续费,计算VIP结束时间
+                    vipEndDate = DateUtils.getDesignDateStr(vipEndDate1, DateUtils.sdfyyyyMMdd, Integer.parseInt(vipDate));
+                    vipStartDate = vipUserData.getVipStartDate();
+                } else {
+                    //过期,删除原来的数据,在重新添加
+                    vipGoodsService.deleteVipUser(vipUserData.getId());
+                    //过期重新开通,设置新的开始和结束时间
+                    vipStartDate = DateUtils.toStringYMD(new Date());
+                    vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+                }
+            }
+
+            VipUserData saveVipUserData = new VipUserData();
+            saveVipUserData.setId(PrimaryIdUtils.getId().toString());
+            saveVipUserData.setUserId(userId);
+            saveVipUserData.setVipGoodsId(vipGoodsId);
+            saveVipUserData.setVipStartDate(vipStartDate);
+            saveVipUserData.setVipEndDate(vipEndDate);
+            saveVipUserData.setCreateTime(LocalDateTime.now());
+            saveVipUserData.setUpdateTime(LocalDateTime.now());
+            vipGoodsService.saveVipUserData(saveVipUserData);
+
+            //修改用户表,添加是VIP用户,VIP最后时间,剩余时长
+            Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
+            SysUser sysUser = sysUserMapper.selectOne(userId);
+            sysUser.setIsVip(true);
+            sysUser.setVipResidueDate(vipResidueDate);
+            sysUser.setVipEndDate(vipEndDate);
+            sysUser.setUpdateTime(new Date());
+            sysUserMapper.update(sysUser);
+            
+            //更新订单状态为已支付
+            vipOrderData.setStatus("2"); // 1:待支付 2:已支付 3:已取消
+            vipOrderData.setUpdateTime(LocalDateTime.now());
+            vipGoodsService.updateVipOrderData(vipOrderData);
+            
+            logger.info("VIP支付成功处理完成,用户ID:{},订单号:{},VIP结束时间:{}", userId, out_trade_no, vipEndDate);
+            
+            return sysUser;
+
+
+        } else {
+            throw new RuntimeException("订单支付失败");
+        }
+    }
+
+    @Override
+    public Transaction queryOrderByOutTradeNo(String out_trade_no) {
+        return queryOrderByOutTradeNo(out_trade_no, "app");
+    }
+    
+    /**
+     * 根据订单号和支付类型查询订单状态
+     * @param out_trade_no 订单号
+     * @param payType 支付类型:app 或 miniprogram
+     * @return 交易信息
+     */
+    public Transaction queryOrderByOutTradeNo(String out_trade_no, String payType) {
+        Config config;
+        
+        if ("miniprogram".equals(payType)) {
+            // 使用小程序配置
+            config = new RSAAutoCertificateConfig.Builder()
+                    .merchantId(wxPayMiniprogramConfig.getMchID())
+                    .privateKeyFromPath(wxPayMiniprogramConfig.getCertPath())
+                    .merchantSerialNumber(wxPayMiniprogramConfig.getMerchantSerialNumber())
+                    .apiV3Key(wxPayMiniprogramConfig.getV3Key())
+                    .build();
+        } else {
+            // 使用APP配置
+            config = new RSAAutoCertificateConfig.Builder()
+                    .merchantId(wxPayAppConfig.getMchID())
+                    .privateKeyFromPath(wxPayAppConfig.getCertPath())
+                    .merchantSerialNumber(wxPayAppConfig.getMerchantSerialNumber())
+                    .apiV3Key(v3Key)
+                    .build();
+        }
+        
+        // 构建service - 对于查询订单,使用JsapiService即可(小程序和APP都支持)
+        JsapiService service = new JsapiService.Builder().config(config).build();
+        com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest();
+        queryOrderByOutTradeNoRequest.setOutTradeNo(out_trade_no);
+        queryOrderByOutTradeNoRequest.setMchid("miniprogram".equals(payType) ? 
+            wxPayMiniprogramConfig.getMchID() : wxPayAppConfig.getMchID());
+            
+        Transaction transaction = service.queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);
+        Transaction.TradeStateEnum tradeState = transaction.getTradeState();
+        logger.info("{}支付订单查询结果 - 订单号:{},状态:{}", payType, out_trade_no, tradeState.name());
+        
+        if ("SUCCESS".equals(tradeState.name())) {
+            logger.info("支付成功:{}", transaction);
+        }
+        return transaction;
+    }
+
+    // ============= 小程序支付相关实现 =============
+    
+    @Override
+    public Object miniprogramUnifiedOrder(String vipGoodsId, String openid) {
+        logger.info("小程序支付基础信息:{}", wxPayMiniprogramConfig);
+        logger.info("用户openid:{}", openid);
+        
+        //查询商品是否存在,存在则下单,不存在则不下单
+        VipGoodsData data = vipGoodsService.getById(vipGoodsId);
+        if (Objects.isNull(data)) {
+            throw new RuntimeException("VIP商品不存在");
+        }
+        
+        if (StringUtils.isBlank(openid)) {
+            throw new RuntimeException("用户openid不能为空");
+        }
+        
+        try {
+            // 使用自动更新平台证书的RSA配置
+            Config config = new RSAAutoCertificateConfig.Builder()
+                    .merchantId(wxPayMiniprogramConfig.getMchID())
+                    .privateKeyFromPath(wxPayMiniprogramConfig.getCertPath())
+                    .merchantSerialNumber(wxPayMiniprogramConfig.getMerchantSerialNumber())
+                    .apiV3Key(wxPayMiniprogramConfig.getV3Key())
+                    .build();
+
+            // 构建service
+            JsapiService service = new JsapiService.Builder().config(config).build();
+            String orderNo = PrimaryIdUtils.getId().toString();
+            
+            // 构建小程序支付请求
+            com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest request = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
+            com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
+            
+            //金额计算,乘以100转换为分
+            String money = data.getVipMoney();
+            int orderAmount = (int) (Double.parseDouble(money) * 100);
+            
+            amount.setTotal(orderAmount);
+            request.setAmount(amount);
+            request.setAppid(wxPayMiniprogramConfig.getAppID());
+            request.setMchid(wxPayMiniprogramConfig.getMchID());
+            request.setDescription(data.getName());
+            request.setNotifyUrl(wxPayMiniprogramConfig.getPayNotifyUrl());
+            request.setOutTradeNo(orderNo);
+            
+            // 设置支付者信息
+            Payer payer = new Payer();
+            payer.setOpenid(openid);
+            request.setPayer(payer);
+            
+            // 调用下单方法,得到应答
+            com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response = service.prepay(request);
+            logger.info("调用微信小程序下单返回的信息是:{}", response);
+            
+            String prepayId = response.getPrepayId();
+            
+            // 构建小程序支付参数
+            MiniprogramPayData payData = new MiniprogramPayData();
+            
+            // 生成时间戳
+            long timestamp = System.currentTimeMillis() / 1000;
+            String timeStamp = String.valueOf(timestamp);
+            
+            // 生成随机字符串
+            String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
+            
+            // 构建package参数
+            String packageParam = "prepay_id=" + prepayId;
+            
+            // 生成签名
+            String signStr = wxPayMiniprogramConfig.getAppID() + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageParam + "\n";
+            SignatureResult signatureResult = config.createSigner().sign(signStr);
+            String paySign = signatureResult.getSign();
+            
+            // 设置小程序支付参数
+            payData.setTimeStamp(timeStamp);
+            payData.setNonceStr(nonceStr);
+            payData.setPackage_(packageParam);
+            payData.setPackageParam(packageParam);  // 设置用于uni.requestPayment的package字段
+            payData.setSignType("MD5");
+            payData.setPaySign(paySign);
+            payData.setOut_trade_no(orderNo);
+            payData.setPrepay_id(prepayId);
+            payData.setDescription(data.getName());
+            payData.setTotal(orderAmount);
+            payData.setRawData(response);
+            
+            logger.info("小程序支付参数:{}", payData);
+            
+            // 通过openid查询用户ID
+            String userId = null;
+            try {
+                SysUser user = sysUserMapper.selectByOpenId(openid);
+                if (user != null) {
+                    userId = user.getId();
+                }
+            } catch (Exception e) {
+                logger.warn("根据openid查询用户失败: {}", openid, e);
+            }
+            
+            // 生成订单记录
+            VipOrderData orderData = new VipOrderData();
+            orderData.setId(PrimaryIdUtils.getId().toString());
+            orderData.setOutTradeNo(orderNo);
+            orderData.setStatus("1");
+            orderData.setVipGoodsId(vipGoodsId);
+            orderData.setUserId(userId);  // 记录用户ID
+            orderData.setOpenid(openid);  // 记录openid
+            orderData.setCreateTime(LocalDateTime.now());
+            orderData.setUpdateTime(LocalDateTime.now());
+            vipGoodsService.insertVipOrderData(orderData);
+            
+            return payData;
+            
+        } catch (Exception e) {
+            logger.error("小程序支付下单失败 - vipGoodsId: {}, openid: {}", vipGoodsId, openid, e);
+            String errorMsg = e.getMessage();
+            if (errorMsg == null || errorMsg.trim().isEmpty()) {
+                errorMsg = e.getClass().getSimpleName() + ": " + e.toString();
+            }
+            throw new RuntimeException("小程序支付下单失败: " + errorMsg);
+        }
+    }
+    
+    @Override
+    public String miniprogramNotify(String notifyStr) {
+        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml>";
+        try {
+            // 转换成map
+            Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
+            WXPay wxpayMiniprogram = new WXPay(wxPayMiniprogramConfig);
+            if (wxpayMiniprogram.isPayResultNotifySignatureValid(resultMap)) {
+                String returnCode = resultMap.get("return_code");  //状态
+                String outTradeNo = resultMap.get("out_trade_no");//商户订单号
+                String transactionId = resultMap.get("transaction_id");
+                if ("SUCCESS".equals(returnCode)) {
+                    if (StringUtils.isNotBlank(outTradeNo)) {
+                        logger.info("小程序支付异步通知成功,订单号:{}", outTradeNo);
+                        
+                        try {
+                            // 查询订单信息
+                            VipOrderData vipOrderData = vipGoodsService.getOrderByOutTradeNo(outTradeNo);
+                            if (vipOrderData != null && "1".equals(vipOrderData.getStatus())) {
+                                // 订单状态为待支付,进行VIP处理
+                                String vipGoodsId = vipOrderData.getVipGoodsId();
+                                String userId = vipOrderData.getUserId();
+                                
+                                if (StringUtils.isNotBlank(userId)) {
+                                    // 有用户ID,执行完整的VIP处理逻辑
+                                    logger.info("开始处理小程序支付异步通知VIP逻辑 - 用户ID: {}, 订单号: {}", userId, outTradeNo);
+                                    
+                                    // 查询VIP商品信息
+                                    VipGoodsData vipGoodsData = vipGoodsService.getById(vipGoodsId);
+                                    if (vipGoodsData != null) {
+                                        // 处理VIP逻辑(复用success方法的逻辑)
+                                        handleVipLogic(userId, vipGoodsId, vipGoodsData, outTradeNo);
+                                        logger.info("小程序支付异步通知VIP处理完成 - 用户ID: {}, 订单号: {}", userId, outTradeNo);
+                                    } else {
+                                        logger.warn("VIP商品不存在 - vipGoodsId: {}, 订单号: {}", vipGoodsId, outTradeNo);
+                                    }
+                                } else {
+                                    logger.warn("订单中缺少用户ID,无法处理VIP逻辑 - 订单号: {}", outTradeNo);
+                                }
+                                
+                                // 更新订单状态为已支付
+                                vipOrderData.setStatus("2"); // 已支付
+                                vipOrderData.setUpdateTime(LocalDateTime.now());
+                                vipGoodsService.updateVipOrderData(vipOrderData);
+                                
+                                logger.info("小程序支付异步通知处理完成,订单号:{}", outTradeNo);
+                            } else {
+                                logger.info("小程序支付异步通知 - 订单已处理或不存在,订单号:{}", outTradeNo);
+                            }
+                        } catch (Exception e) {
+                            logger.error("小程序支付异步通知业务处理失败,订单号:{}", outTradeNo, e);
+                            // 即使业务处理失败,也要返回SUCCESS,避免微信重复通知
+                        }
+                        
+                        xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
+                    }
+                }
+            } else {
+                logger.warn("小程序支付异步通知签名验证失败");
+            }
+        } catch (Exception e) {
+            logger.error("小程序支付通知处理失败", e);
+        }
+        return xmlBack;
+    }
+    
+    @Override
+    public SysUser miniprogramSuccess(String vipGoodsId, String out_trade_no) {
+        //查询商品是否存在,存在则下单,不存在则不下单
+        VipGoodsData data = vipGoodsService.getById(vipGoodsId);
+        if (Objects.isNull(data)) {
+            throw new RuntimeException("VIP商品不存在");
+        }
+
+        VipOrderData vipOrderData = vipGoodsService.getOrderByOutTradeNo(out_trade_no);
+        if (Objects.isNull(vipOrderData)) {
+            throw new RuntimeException("订单不存在");
+        }
+        
+        //查询订单是否支付成功,支付成功:生成VIP时长,失败异常
+        Transaction transaction = queryOrderByOutTradeNo(out_trade_no, "miniprogram");
+
+        Transaction.TradeStateEnum tradeState = transaction.getTradeState();
+        logger.info("小程序支付订单查询结果 - 订单号:{},状态:{}", out_trade_no, tradeState.name());
+
+        if ("SUCCESS".equals(tradeState.name())) {
+            logger.info("小程序支付成功:{}", transaction);
+            //查询用户是否已经开通过,开通过则续费,没有开通过怎插新数据
+            String userId = TokenUtils.getUserId();
+            VipUserData vipUserData = vipGoodsService.selectByUserId(userId, vipGoodsId);
+            //VIP时长
+            String vipStartDate = "";
+            String vipEndDate = "";
+            String vipDate = data.getVipDate();
+            if (Objects.isNull(vipUserData)) {
+                //没有开通过,新开通,开始时间=当天时间,结束时间=会员时长+当前时间
+                vipStartDate = DateUtils.toStringYMD(new Date());
+                vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+
+            } else {
+                //开通过,查看有没有过期,过期:新开通,不过期,续费
+                String vipEndDateStr = vipUserData.getVipEndDate();
+                Date vipEndDate1 = DateUtils.toDateYMDHMS(vipEndDateStr);
+                Date todayDate = DateUtils.toDateYMDHMS(DateUtils.toStringYMD(new Date()));
+                int compareTo = todayDate.compareTo(vipEndDate1);
+                //0:到当天 1:过期 -1:不过期
+                if (compareTo == 0 || compareTo == -1) {
+                    //不过期,续费,计算VIP结束时间
+                    vipEndDate = DateUtils.getDesignDateStr(vipEndDate1, DateUtils.sdfyyyyMMdd, Integer.parseInt(vipDate));
+                    vipStartDate = vipUserData.getVipStartDate();
+                } else {
+                    //过期,删除原来的数据,在重新添加
+                    vipGoodsService.deleteVipUser(vipUserData.getId());
+                    //过期重新开通,设置新的开始和结束时间
+                    vipStartDate = DateUtils.toStringYMD(new Date());
+                    vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+                }
+            }
+
+            VipUserData saveVipUserData = new VipUserData();
+            saveVipUserData.setId(PrimaryIdUtils.getId().toString());
+            saveVipUserData.setUserId(userId);
+            saveVipUserData.setVipGoodsId(vipGoodsId);
+            saveVipUserData.setVipStartDate(vipStartDate);
+            saveVipUserData.setVipEndDate(vipEndDate);
+            saveVipUserData.setCreateTime(LocalDateTime.now());
+            saveVipUserData.setUpdateTime(LocalDateTime.now());
+            vipGoodsService.saveVipUserData(saveVipUserData);
+
+            //修改用户表,添加是VIP用户,VIP最后时间,剩余时长
+            Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
+            SysUser sysUser = sysUserMapper.selectOne(userId);
+            sysUser.setIsVip(true);
+            sysUser.setVipResidueDate(vipResidueDate);
+            sysUser.setVipEndDate(vipEndDate);
+            sysUser.setUpdateTime(new Date());
+            sysUserMapper.update(sysUser);
+            
+            //更新订单状态为已支付
+            vipOrderData.setStatus("2"); // 1:待支付 2:已支付 3:已取消
+            vipOrderData.setUpdateTime(LocalDateTime.now());
+            vipGoodsService.updateVipOrderData(vipOrderData);
+            
+            logger.info("小程序VIP支付成功处理完成,用户ID:{},订单号:{},VIP结束时间:{}", userId, out_trade_no, vipEndDate);
+            
+            return sysUser;
+
+        } else {
+            throw new RuntimeException("小程序订单支付失败,状态:" + tradeState.name());
+        }
+    }
+    
+    /**
+     * 处理VIP逻辑(抽取的公共方法,供success和异步通知使用)
+     * @param userId 用户ID
+     * @param vipGoodsId VIP商品ID
+     * @param vipGoodsData VIP商品信息
+     * @param outTradeNo 订单号
+     */
+    private void handleVipLogic(String userId, String vipGoodsId, VipGoodsData vipGoodsData, String outTradeNo) {
+        try {
+            //查询用户是否已经开通过,开通过则续费,没有开通过则插新数据
+            VipUserData vipUserData = vipGoodsService.selectByUserId(userId, vipGoodsId);
+            //VIP时长
+            String vipStartDate = "";
+            String vipEndDate = "";
+            String vipDate = vipGoodsData.getVipDate();
+            
+            if (Objects.isNull(vipUserData)) {
+                //没有开通过,新开通,开始时间=当天时间,结束时间=会员时长+当前时间
+                vipStartDate = DateUtils.toStringYMD(new Date());
+                vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+            } else {
+                //开通过,查看有没有过期,过期:新开通,不过期,续费
+                String vipEndDateStr = vipUserData.getVipEndDate();
+                Date vipEndDate1 = DateUtils.toDateYMDHMS(vipEndDateStr);
+                Date todayDate = DateUtils.toDateYMDHMS(DateUtils.toStringYMD(new Date()));
+                int compareTo = todayDate.compareTo(vipEndDate1);
+                //0:到当天 1:过期 -1:不过期
+                if (compareTo == 0 || compareTo == -1) {
+                    //不过期,续费,计算VIP结束时间
+                    vipEndDate = DateUtils.getDesignDateStr(vipEndDate1, DateUtils.sdfyyyyMMdd, Integer.parseInt(vipDate));
+                    vipStartDate = vipUserData.getVipStartDate();
+                } else {
+                    //过期,删除原来的数据,在重新添加
+                    vipGoodsService.deleteVipUser(vipUserData.getId());
+                    //过期重新开通,设置新的开始和结束时间
+                    vipStartDate = DateUtils.toStringYMD(new Date());
+                    vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
+                }
+            }
+
+            VipUserData saveVipUserData = new VipUserData();
+            saveVipUserData.setId(PrimaryIdUtils.getId().toString());
+            saveVipUserData.setUserId(userId);
+            saveVipUserData.setVipGoodsId(vipGoodsId);
+            saveVipUserData.setVipStartDate(vipStartDate);
+            saveVipUserData.setVipEndDate(vipEndDate);
+            saveVipUserData.setCreateTime(LocalDateTime.now());
+            saveVipUserData.setUpdateTime(LocalDateTime.now());
+            vipGoodsService.saveVipUserData(saveVipUserData);
+
+            //修改用户表,添加是VIP用户,VIP最后时间,剩余时长
+            Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
+            SysUser sysUser = sysUserMapper.selectOne(userId);
+            if (sysUser != null) {
+                sysUser.setIsVip(true);
+                sysUser.setVipResidueDate(vipResidueDate);
+                sysUser.setVipEndDate(vipEndDate);
+                sysUser.setUpdateTime(new Date());
+                sysUserMapper.update(sysUser);
+                
+                logger.info("VIP处理完成 - 用户ID: {}, 订单号: {}, VIP结束时间: {}", userId, outTradeNo, vipEndDate);
+            } else {
+                logger.warn("用户不存在,无法更新VIP状态 - 用户ID: {}", userId);
+            }
+        } catch (Exception e) {
+            logger.error("VIP逻辑处理失败 - 用户ID: {}, 订单号: {}", userId, outTradeNo, e);
+            throw e;
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+
+
+/** Native 支付下单为例 */
+
+
+        /*  *//** 商户号 *//*
+        String merchantId = "1659536133";
+        *//** 商户API私钥路径 *//*
+        String privateKeyPath = "D:\\soft\\WXCertUtil\\WXCertUtil\\cert\\1659536133_20240224_cert\\apiclient_key.pem";
+        *//** 商户证书序列号 *//*
+        String merchantSerialNumber = "2C9364545EF9C4C82700563B8C398451E14743B3";
+        *//** 商户APIV3密钥 *//*
+        String apiV3Key = "A123456789B123456789C1234567890D";
+
+        // 使用自动更新平台证书的RSA配置
+        // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
+        Config config =
+                new RSAAutoCertificateConfig.Builder()
+                        .merchantId(merchantId)
+                        .privateKeyFromPath(privateKeyPath)
+                        .merchantSerialNumber(merchantSerialNumber)
+                        .apiV3Key(apiV3Key)
+                        .build();
+        // 构建service
+        AppService service = new AppService.Builder().config(config).build();
+        // request.setXxx(val)设置所需参数,具体参数可见Request定义
+
+        String orderNo = PrimaryIdUtils.getId().toString();
+        PrepayRequest request = new PrepayRequest();
+        Amount amount = new Amount();
+        amount.setTotal(100);
+        request.setAmount(amount);
+        request.setAppid("wx8fdb27b97ed17533");
+        request.setMchid("1659536133");
+        request.setDescription("VIP一个月商品");
+        request.setNotifyUrl("http://120.46.40.61:18885/api/swagger-ui.html");
+        request.setOutTradeNo(orderNo);
+        // 调用下单方法,得到应答
+        PrepayResponse response = service.prepay(request);
+        // 使用微信扫描 code_url 对应的二维码,即可体验Native支付
+        System.out.println(">>>>>>>>>>>:" + response);*/
+
+
+        String noncestr = UUID.randomUUID().toString().replaceAll("-", "");
+        long currentTimeMillis = System.currentTimeMillis() / 1000;
+        System.out.println(currentTimeMillis);
+        // System.out.println(noncestr.length());
+
+
+    /*    Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.DAY_OF_MONTH, 2);
+
+        int year = calendar.get(Calendar.YEAR);
+        int month = calendar.get(Calendar.MONTH) + 1;
+        int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+        System.out.println("7天后的日期为:" + year + "-" + month + "-" + day);*/
+
+        //Base64.getDecoder().decode("NDU1ODAwNjk2M0QyOTE2QTMwNjBGNUQwMTEwNkU4ODA0OTcyQTkyOTVDRkJGRTYzQUMzOUYyRkE5Q0RGQ0Y1Qg==");
+
+
+        String vipEndDateStr = "2024-03-3";
+        Date vipEndDate = DateUtils.toDateYMDHMS(vipEndDateStr);
+        Date date = DateUtils.toDateYMDHMS(DateUtils.toStringYMD(new Date()));
+        int compareTo = vipEndDate.compareTo(date);
+        System.out.println(compareTo);
+
+
+    }
+
+  /*  public Transaction queryOrderByOutTradeNo(QueryOrderByOutTradeNoRequest request) {
+        String requestPath =
+                "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}";
+        QueryOrderByOutTradeNoRequest realRequest = request;
+        // 添加 path param
+        requestPath =
+                requestPath.replace("{" + "out_trade_no" + "}", urlEncode(realRequest.getOutTradeNo()));
+
+        // 添加 query param
+        QueryParameter queryParameter = new QueryParameter();
+        if (realRequest.getMchid() != null) {
+            queryParameter.add("mchid", urlEncode(realRequest.getMchid()));
+        }
+        requestPath += queryParameter.getQueryStr();
+        HttpHeaders headers = new HttpHeaders();
+        headers.addHeader(Constant.ACCEPT, MediaType.APPLICATION_JSON.getValue());
+        headers.addHeader(Constant.CONTENT_TYPE, MediaType.APPLICATION_JSON.getValue());
+        HttpRequest httpRequest =
+                new HttpRequest.Builder()
+                        .httpMethod(HttpMethod.GET)
+                        .url(requestPath)
+                        .headers(headers)
+                        .build();
+
+        HttpResponse<Transaction> httpResponse = httpClient.execute(httpRequest, Transaction.class);
+        return httpResponse.getServiceResponse();
+    }*/
+
+
+}
+
+
+

+ 269 - 0
src/main/java/com/dtb/portal/util/AliyunOSSUtil.java

@@ -0,0 +1,269 @@
+package com.dtb.portal.util;
+
+import com.aliyun.oss.*;
+import com.aliyun.oss.event.ProgressEvent;
+import com.aliyun.oss.event.ProgressEventType;
+import com.aliyun.oss.event.ProgressListener;
+import com.aliyun.oss.model.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import com.dtb.portal.config.AliyunConfig;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author:slambb
+ * @date:2019/12/16
+ */
+@Service
+@Slf4j
+public class AliyunOSSUtil implements ProgressListener {
+
+    @Autowired
+    private AliyunConfig aliyunConfig;
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+    private long bytesWritten = 0;
+    private long totalBytes = -1;
+    private boolean succeed = false;
+    private String fileKey;
+    private static long redisExpire = 7200L;
+
+    /**
+     * 上传
+     * @param file
+     * @return
+     */
+    public String upload(File file,String fileType,String fileKey){
+        log.info("=========>OSS文件上传开始:"+file.getName());
+        this.fileKey = fileKey;
+        String endpoint= aliyunConfig.getEndpoint();
+        String accessKeyId= aliyunConfig.getAccessKeyId();
+        String accessKeySecret= aliyunConfig.getAccessKeySecret();
+        String bucketName= aliyunConfig.getBucketName();
+        String fileHost= aliyunConfig.getFileHost();
+        if(fileType.equals("image")){
+            //如果是图片,保存到图片文件夹里面
+            fileHost = aliyunConfig.getFileImages();
+        }else if(fileType.equals("video")){
+            fileHost = aliyunConfig.getFileVideos();
+        }
+        log.info(endpoint+"=========>OSS文件上传开始 fileHost:"+fileHost);
+
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+        String dateStr = format.format(new Date());
+
+        if(null == file){
+            return null;
+        }
+
+        OSS ossClient = new OSSClientBuilder().build(endpoint,accessKeyId,accessKeySecret);
+        try {
+            //容器不存在,就创建
+            if(! ossClient.doesBucketExist(bucketName)){
+                ossClient.createBucket(bucketName);
+                CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
+                createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
+                ossClient.createBucket(createBucketRequest);
+            }
+            //创建文件路径
+            String fileUrl = fileHost+"/"+(dateStr + "/" + UUID.randomUUID().toString().replace("-","")+"-"+file.getName());
+            //上传文件
+            PutObjectResult result = ossClient.putObject(new PutObjectRequest(bucketName, fileUrl, file).<PutObjectRequest>withProgressListener(new AliyunOSSUtil()));
+            //设置权限 这里是公开读
+            ossClient.setBucketAcl(bucketName,CannedAccessControlList.PublicRead);
+            if(null != result){
+                log.info("==========>OSS文件上传成功,OSS地址:"+fileUrl);
+                return fileUrl;
+            }
+        }catch (OSSException oe){
+            log.error(oe.getMessage());
+        }catch (ClientException ce){
+            log.error(ce.getMessage());
+        }finally {
+            //关闭
+            ossClient.shutdown();
+        }
+        return null;
+    }
+
+
+    /**
+     * 删除
+     * @param fileKey
+     * @return
+     */
+    @Transactional
+    public Boolean deleteBlog(String fileKey){
+        log.info("=========>OSS文件删除开始");
+        String endpoint= aliyunConfig.getEndpoint();
+        String accessKeyId= aliyunConfig.getAccessKeyId();
+        String accessKeySecret= aliyunConfig.getAccessKeySecret();
+        String bucketName= aliyunConfig.getBucketName();
+        String fileHost= aliyunConfig.getFileHost();
+
+        try {
+            OSS ossClient = new OSSClientBuilder().build(endpoint,accessKeyId,accessKeySecret);
+
+            if(!ossClient.doesBucketExist(bucketName)){
+                log.info("==============>您的Bucket不存在");
+                return false;
+            }else {
+                log.info("==============>开始删除Object");
+                ossClient.deleteObject(bucketName,fileKey);
+                log.info("==============>Object删除成功:"+fileKey);
+                return true;
+            }
+        }catch (Exception ex){
+            log.info("删除Object失败",ex);
+            return false;
+        }
+    }
+
+    /**
+     * 拼接并,返回oss 默认域名
+     * @param fileKey
+     * @return
+     */
+    public String addDomainName(String fileKey){
+        if(StringUtils.isEmpty(fileKey))return fileKey;
+        Boolean bAdd = isHttpUrl(fileKey);
+        if(!bAdd){
+            if(aliyunConfig.getUsingDomain()){
+                String url = aliyunConfig.getDomainName()
+                        .concat("/")
+                        .concat(fileKey);
+                return url;
+            }else{
+                String url = "https://"
+                        .concat(aliyunConfig.getBucketName())
+                        .concat(".")
+                        .concat(aliyunConfig.getEndpoint())
+                        .concat("/")
+                        .concat(fileKey);
+                return url;
+            }
+
+        }else{
+            return fileKey;
+        }
+
+    }
+
+
+    public String getDomainName(){
+        if(aliyunConfig.getUsingDomain()){
+            String url = aliyunConfig.getDomainName()
+                    .concat("/");
+            return url;
+        }else{
+            String url = "https://"
+                    .concat(aliyunConfig.getBucketName())
+                    .concat(".")
+                    .concat(aliyunConfig.getEndpoint())
+                    .concat("/");
+            return url;
+        }
+    }
+    /**
+     * 判断字符串是否为URL
+     * @param urls 用户头像key
+     * @return true:是URL、false:不是URL
+     */
+    public static boolean isHttpUrl(String urls) {
+        boolean isurl = false;
+        String regex = "(((https|http)?://)?([a-z0-9]+[.])|(www.))"
+                + "\\w+[.|\\/]([a-z0-9]{0,})?[[.]([a-z0-9]{0,})]+((/[\\S&&[^,;\u4E00-\u9FA5]]+)+)?([.][a-z0-9]{0,}+|/?)";//设置正则表达式
+
+        Pattern pat = Pattern.compile(regex.trim());//比对
+        Matcher mat = pat.matcher(urls.trim());
+        isurl = mat.matches();//判断是否匹配
+        if (isurl) {
+            isurl = true;
+        }
+        return isurl;
+    }
+    /**
+     * 查询文件名列表
+     * @param bucketName
+     * @return
+     */
+    public List<String> getObjectList(String bucketName){
+        List<String> listRe = new ArrayList<>();
+        String endpoint= aliyunConfig.getEndpoint();
+        String accessKeyId= aliyunConfig.getAccessKeyId();
+        String accessKeySecret= aliyunConfig.getAccessKeySecret();
+        try {
+            log.info("===========>查询文件名列表");
+            OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+            ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName);
+            //列出11111目录下今天所有文件
+            listObjectsRequest.setPrefix("11111/"+format.format(new Date())+"/");
+            ObjectListing list = ossClient.listObjects(listObjectsRequest);
+            for(OSSObjectSummary objectSummary : list.getObjectSummaries()){
+                System.out.println(objectSummary.getKey());
+                listRe.add(objectSummary.getKey());
+            }
+            return listRe;
+        }catch (Exception ex){
+            log.info("==========>查询列表失败",ex);
+            return new ArrayList<>();
+        }
+    }
+
+    @Override
+    public void progressChanged(ProgressEvent progressEvent) {
+        long bytes = progressEvent.getBytes();
+        ProgressEventType eventType = progressEvent.getEventType();
+        switch (eventType) {
+            case TRANSFER_STARTED_EVENT:
+                System.out.println("Start to upload......");
+                // oss上传进度存储到redis,并设置有效期
+                redisTemplate.opsForValue().set(String.valueOf(fileKey), "0", redisExpire, TimeUnit.SECONDS);
+                break;
+            case REQUEST_CONTENT_LENGTH_EVENT:
+                this.totalBytes = bytes;
+                // oss上传进度存储到redis,并设置有效期
+                redisTemplate.opsForValue().set(String.valueOf(fileKey), "0", redisExpire, TimeUnit.SECONDS);
+                System.out.println(this.totalBytes + " bytes in total will be uploaded to OSS");
+                break;
+            case REQUEST_BYTE_TRANSFER_EVENT:
+                this.bytesWritten += bytes;
+                if (this.totalBytes != -1) {
+                    int percent = (int)(this.bytesWritten * 100.0 / this.totalBytes);
+                    // oss上传进度存储到redis,并设置有效期
+                    redisTemplate.opsForValue().set(String.valueOf(fileKey), String.valueOf(percent), redisExpire, TimeUnit.SECONDS);
+                    System.out.println(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
+                } else {
+                    // oss上传进度存储到redis,并设置有效期
+                    redisTemplate.opsForValue().set(String.valueOf(fileKey), "0", redisExpire, TimeUnit.SECONDS);
+                    System.out.println(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
+                }
+                break;
+            case TRANSFER_COMPLETED_EVENT:
+                this.succeed = true;
+                // oss上传进度存储到redis,并设置有效期
+                redisTemplate.opsForValue().set(String.valueOf(fileKey), "100", redisExpire, TimeUnit.SECONDS);
+                System.out.println("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
+                break;
+            case TRANSFER_FAILED_EVENT:
+                System.out.println("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
+                break;
+            default:
+                break;
+        }
+    }
+}

+ 56 - 0
src/main/java/com/dtb/portal/util/AliyunSMSUtil.java

@@ -0,0 +1,56 @@
+package com.dtb.portal.util;
+
+import com.dtb.portal.config.AliyunConfig;
+import com.aliyuncs.CommonRequest;
+import com.aliyuncs.CommonResponse;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.exceptions.ServerException;
+import com.aliyuncs.http.MethodType;
+import com.aliyuncs.profile.DefaultProfile;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author:slambb
+ * @date:2020/1/8
+ */
+@Service
+public class AliyunSMSUtil {
+
+    @Autowired
+    private AliyunConfig aliyunConfig;
+
+    public String sendSms(String phoneNumber,String key){
+        DefaultProfile profile = DefaultProfile.getProfile(
+                aliyunConfig.getRegionId(),
+                aliyunConfig.getAccessKeyId(),
+                aliyunConfig.getAccessKeySecret());
+
+        IAcsClient client = new DefaultAcsClient(profile);
+
+        CommonRequest request = new CommonRequest();
+        request.setMethod(MethodType.POST);
+        request.setDomain("dysmsapi.aliyuncs.com");
+        request.setVersion("2017-05-25");
+        request.setAction("SendSms");
+        request.putQueryParameter("RegionId", aliyunConfig.getRegionId());
+        request.putQueryParameter("PhoneNumbers", phoneNumber);
+        request.putQueryParameter("SignName", aliyunConfig.getSignName());
+        request.putQueryParameter("TemplateCode", aliyunConfig.getTemplateCode());
+        request.putQueryParameter("TemplateParam", "{code:"+key+"}");
+
+        try {
+            CommonResponse response = client.getCommonResponse(request);
+            System.out.println(response.getData());
+            return response.getData();
+        } catch (ServerException e) {
+            e.printStackTrace();
+        } catch (ClientException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+}

+ 148 - 0
src/main/java/com/dtb/portal/util/CodeDefault.java

@@ -0,0 +1,148 @@
+package com.dtb.portal.util;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 返回的错误代码, 返回前端用 int, 后端用枚举, 可以减少数据传输
+ * 可以基于 code 的 name 进行国际化
+ * 这里只列出系统默认的 code, 其他的 code, 应该按 CodePerson 这样进行区分
+ * 建议划分方法, 使用 9 位数字 (一个 int 可以表示完全), 第一位固定是 1, 接下来 4 位划分模块, 最后四位划分具体的 errorCode;
+ * 如: 100010001, 表示系统错误
+ *
+ * @author MoSence
+ */
+@AllArgsConstructor
+public enum CodeDefault implements CodeEnum {
+    /**
+     * 成功
+     */
+    OK(0, "成功"),
+    /**
+     * 成功
+    SESSION_EXPIRE(-2, "会话超时,请重新登录"),
+    /**
+     * 部分成功
+     */
+    PARTIAL_SUCCESS(100010001L, "部分成功"),
+    /**
+     * 未处理异常
+     */
+    INTERNAL_SERVER_ERROR(100010002L, "未处理异常"),
+    /**
+     * 客户端输入参数错误
+     */
+    ILLEGAL_ARGUMENT(100010003L, "客户端输入参数错误"),
+    /**
+     * 主键生成错误
+     */
+    PRIMARY_ID_ERROR(100010004L, "主键生成错误"),
+    /**
+     * 日期格式错误
+     */
+    ILLEGAL_DATE_FORMAT(100010005L, "日期格式错误"),
+
+    /**
+     * 数据不存在
+     */
+    DATA_NOEXIT(100000005, "数据不存在"),
+
+    /**
+     * 数据不存在
+     */
+    PARA_NOEXIT(1000000010, "参数不存在"),
+
+    /**
+     * 数据不存在
+     */
+    DATA_EXIT(100000005, "数据存在"),
+    /**
+     * 空值异常
+     */
+    NULL_POINT_ERROR(100010006L, "空值异常"),
+    /**
+     * 验证生成异常
+     */
+    CODE_CREATE_ERROR(100010007L, "验证生成异常"),
+
+    /**
+     * 登录失效
+     */
+    LOGIN_FAIL_INPUT_ISNULL(4004, "登录失败,登录输入项为空"),
+
+    /**
+     * 登录失效
+     */
+    LOGIN_FAIL_CHECKCODE_INVALID(4005, "登录失败,验证码无效"),
+
+    /**
+     * 登录失效
+     */
+    LOGIN_FAIL_USERNAME_PASSWORD_ERROR(4001, "登录失败,用户名或密码错误"),
+    /**
+     * 登录失效
+     */
+    LOGIN_FAIL_USER_IS_DISABLED(4003, "登录失败,用户无效"),
+    /**
+     * 登录失效
+     */
+    LOGIN_FAIL_USER_IS_LOCK(4002, "登录失败,用户已锁定"),
+    /**
+     * 登录失效
+     */
+    SYSTEM_ERROR(100000000, "系统异常"),
+
+    /**
+     * 认证失败
+     */
+    AUTH_FAIL(403, "认证失败,请求无效"),
+    /**
+     * 添加数据失败
+     */
+    INSERT_FAIL(500, "添加数据失败"),
+    /**
+     * 添加文件失败
+     */
+    INSERT_FILE_FAIL(501, "添加文件失败"),
+
+
+    /**
+     * 文件类型找不到
+     */
+    FILE_TYPE_NO_FOUND_FAIL(509, "文件类型找不到"),
+
+
+    /**
+     * 文件找不到
+     */
+    FILE_NO_FOUND_FAIL(510, "文件类型找不到"),
+    /**
+     * 更新失败
+     */
+    UPDATE_FAIL(502, "更新失败"),
+    /**
+     * 用户类型没有失败
+     */
+    USER_TYPE_NO_FOUND_FAIL(501, "该用户不是管理员类型"),
+
+    NO_USERPOOL_ID_FAIL(503, "没有用户池ID"),
+
+    LIST_IS_NULL_FAIL(504, "集合为null异常"),
+
+    PASSWORD_NOT_IDENTICAL_FAIL(505, "两次收入密码不相同"),
+    ;
+
+    /**
+     * 返回客户端的编码
+     */
+    @Getter
+    private final long code;
+
+    /**
+     * 默认消息
+     */
+    @Getter
+    private final String defaultMessage;
+
+
+}

+ 23 - 0
src/main/java/com/dtb/portal/util/CodeEnum.java

@@ -0,0 +1,23 @@
+package com.dtb.portal.util;
+
+/**
+ * @author :mosence
+ * @date :2019/12/30
+ */
+public interface CodeEnum {
+    /**
+     * 消息代码
+     * 注意不要设计出越界CODE(long类型)
+     * [-9223372036854775808,9223372036854775807]
+     *
+     * @return 消息代码
+     */
+    long getCode();
+
+    /**
+     * 默认消息内容
+     *
+     * @return 默认消息内容
+     */
+    String getDefaultMessage();
+}

+ 123 - 0
src/main/java/com/dtb/portal/util/DateUtils.java

@@ -0,0 +1,123 @@
+package com.dtb.portal.util;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+@Slf4j
+public class DateUtils {
+    public final static String sdfyyyyMMdd = "yyyy-MM-dd";
+
+
+    public final static String sdfyyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss";
+
+    /**
+     * 获取date天后的时间
+     *
+     * @param date
+     * @return
+     */
+    public static String getAfterDate(int date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.DAY_OF_MONTH, date);
+        int year = calendar.get(Calendar.YEAR);
+        int month = calendar.get(Calendar.MONTH) + 1;
+        int day = calendar.get(Calendar.DAY_OF_MONTH);
+        return String.format("%s-%s-%s", year, month, day);
+    }
+
+    public static Date toDateYMDHMS(String str) {
+        SimpleDateFormat sdf = new SimpleDateFormat(sdfyyyyMMdd);
+        try {
+            return sdf.parse(str);
+        } catch (ParseException e) {
+            throw new RuntimeException("格式化时间失败");
+        }
+    }
+
+    public static String toStringYMD(Date date) {
+        if (date == null) {
+            return "";
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat(sdfyyyyMMdd);
+        return sdf.format(date);
+    }
+
+    /**
+     * 获取指定日期往前/往后指定天数的日期
+     *
+     * @param now    指定日期 不传则为当前日期
+     * @param format 时间格式
+     * @param day    指定天数 指定当前历史日期时传负数,未来日期传正数
+     * @return 指定天数前的日期
+     */
+    public static String getDesignDateStr(Date now, String format, int day) {
+
+        Calendar calendar = Calendar.getInstance();
+        if (null != now) {
+            calendar.setTime(now);
+        }
+
+        calendar.add(Calendar.DATE, day);
+
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        return sdf.format(calendar.getTime());
+    }
+
+    public static Date getDesignDate(Date now, String format, int day) {
+
+        Calendar calendar = Calendar.getInstance();
+        if (null != now) {
+            calendar.setTime(now);
+        }
+
+        calendar.add(Calendar.DATE, day);
+
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        return calendar.getTime();
+    }
+
+
+    /**
+     * 计算2个Date差几天
+     *
+     * @param date1
+     * @param date2
+     * @return
+     */
+    public static Long getDifferenceTime(Date date1, Date date2) {
+        //4、计算两个日期之间有多少天?
+        Calendar c = Calendar.getInstance();
+        Calendar c1 = Calendar.getInstance();
+        //设置两个日期
+        c.setTime(date1);
+        c1.setTime(date2);
+        //将日期转换为Date类型,便于获取时间戳
+        Date a1 = c.getTime();
+        Date a2 = c1.getTime();
+        //getTime方法返回自 1970 年 1 月 1 日 00:00:00 GMT 以来,此 Date 对象表示的毫秒数
+        long l = 1000 * 60 * 60 * 24;
+        //用返回的data对象进行获取毫秒操作
+        long d1 = a1.getTime() / l;
+        long d2 = a2.getTime() / l;
+        return d2 - d1;
+    }
+
+
+    public static void main(String[] args) {
+        String vipEndDate = DateUtils.getAfterDate(Integer.parseInt("2"));
+        System.out.println(vipEndDate);
+        Date date2 = DateUtils.toDateYMDHMS(vipEndDate);
+        String startDate = DateUtils.toStringYMD(new Date());
+        System.out.println(startDate);
+        Long differenceTime = getDifferenceTime(new Date(), date2);
+        System.out.println(differenceTime);
+       // System.out.println(vipEndDate);
+    }
+
+
+}

+ 48 - 0
src/main/java/com/dtb/portal/util/HttpClientUtils.java

@@ -0,0 +1,48 @@
+package com.dtb.portal.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.wechat.pay.java.core.http.Constant;
+import okhttp3.*;
+
+import java.util.Map;
+import java.util.Objects;
+
+public class HttpClientUtils {
+
+    public static Object getOAuthAccessTokenByPostJson(Map<String, String> params) {
+
+
+        String url = params.get("url");
+        String authorization = params.get("authorization");
+
+        JSONObject jsonObject = new JSONObject();
+        //jsonObject.put("")
+/*        jsonObject.put(CommonConstant.CLIENT_ID, oauth2AppRequest.getClientId());
+        jsonObject.put(CommonConstant.CLIENT_SECRET, oauth2AppRequest.getClientSecret());
+        jsonObject.put(CommonConstant.REDIRECT_URI, oauth2AppRequest.getRedirectUri());
+        jsonObject.put(CommonConstant.GRANT_TYPE, oauth2AppRequest.getGrantType());
+        jsonObject.put(CommonConstant.CODE, oauth2AppRequest.getCode());*/
+
+        try {
+            MediaType mediaType = MediaType.parse("application/json");
+            RequestBody requestBody = RequestBody.create(mediaType, jsonObject.toJSONString());
+            Request request = new Request.Builder()
+                    .url(url)
+                    .addHeader("Content-Type", "application/json")
+                    .addHeader(Constant.AUTHORIZATION, authorization)
+                    .post(requestBody)
+                    .build();
+            OkHttpClient okHttpClient = new OkHttpClient();
+            Call call = okHttpClient.newCall(request);
+            Response response = call.execute();
+            String jsonStr = Objects.requireNonNull(response.body()).string();
+            return JSON.toJavaObject(JSONObject.parseObject(jsonStr), Object.class);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("获取token信息失败");
+        }
+    }
+
+
+}

+ 107 - 0
src/main/java/com/dtb/portal/util/MobileUtil.java

@@ -0,0 +1,107 @@
+package com.dtb.portal.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.regex.Pattern;
+
+/**
+ * @author fuzhiyou
+ * @date 2022-04-14 16:02
+ * <p>
+ * 验证手机号工具类
+ */
+@Slf4j
+public class MobileUtil {
+    /**
+     * 中国电信号码格式验证 手机段: 133,149,153,173,177,180,181,189,199,1349,1410,1700,1701,1702
+     **/
+    private static final String CHINA_TELECOM_PATTERN = "(?:^(?:\\+86)?1(?:33|49|53|7[37]|8[019]|99)\\d{8}$)|(?:^(?:\\+86)?1349\\d{7}$)|(?:^(?:\\+86)?1410\\d{7}$)|(?:^(?:\\+86)?170[0-2]\\d{7}$)";
+
+    /**
+     * 中国联通号码格式验证 手机段:130,131,132,145,146,155,156,166,171,175,176,185,186,1704,1707,1708,1709
+     **/
+    private static final String CHINA_UNICOM_PATTERN = "(?:^(?:\\+86)?1(?:3[0-2]|4[56]|5[56]|66|7[156]|8[56])\\d{8}$)|(?:^(?:\\+86)?170[47-9]\\d{7}$)";
+
+    /**
+     * 中国移动号码格式验证
+     * 手机段:134,135,136,137,138,139,147,148,150,151,152,157,158,159,178,182,183,184,187,188,195,198,1440,1703,1705,1706
+     **/
+    private static final String CHINA_MOBILE_PATTERN = "(?:^(?:\\+86)?1(?:3[4-9]|4[78]|5[0-27-9]|78|8[2-478]|98|95)\\d{8}$)|(?:^(?:\\+86)?1440\\d{7}$)|(?:^(?:\\+86)?170[356]\\d{7}$)";
+
+    /**
+     * 中国大陆手机号码校验
+     *
+     * @param phone
+     * @return
+     */
+    public static boolean checkPhone(String phone) {
+        if (StringUtils.isNotBlank(phone)) {
+            if (checkChinaMobile(phone) || checkChinaUnicom(phone) || checkChinaTelecom(phone)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 中国移动手机号码校验
+     *
+     * @param phone
+     * @return
+     */
+    public static boolean checkChinaMobile(String phone) {
+        if (StringUtils.isNotBlank(phone)) {
+            Pattern regexp = Pattern.compile(CHINA_MOBILE_PATTERN);
+            if (regexp.matcher(phone).matches()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 中国联通手机号码校验
+     *
+     * @param phone
+     * @return
+     */
+    public static boolean checkChinaUnicom(String phone) {
+        if (StringUtils.isNotBlank(phone)) {
+            Pattern regexp = Pattern.compile(CHINA_UNICOM_PATTERN);
+            if (regexp.matcher(phone).matches()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 中国电信手机号码校验
+     *
+     * @param phone
+     * @return
+     */
+    public static boolean checkChinaTelecom(String phone) {
+        if (StringUtils.isNotBlank(phone)) {
+            Pattern regexp = Pattern.compile(CHINA_TELECOM_PATTERN);
+            if (regexp.matcher(phone).matches()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 隐藏手机号中间四位
+     *
+     * @param phone
+     * @return java.lang.String
+     */
+    public static String hideMiddleMobile(String phone) {
+        if (StringUtils.isNotBlank(phone)) {
+            phone = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
+        }
+        return phone;
+    }
+}

+ 41 - 0
src/main/java/com/dtb/portal/util/PrimaryIdUtils.java

@@ -0,0 +1,41 @@
+package com.dtb.portal.util;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.FastDateFormat;
+
+import java.security.SecureRandom;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * 主键生成器
+ */
+@Slf4j
+public class PrimaryIdUtils {
+
+    /**
+     * 获取全局唯一主键,递增
+     *
+     * @return
+     */
+    public static Long getId() {
+
+        try {
+            Date date = new Date();
+            SecureRandom number = SecureRandom.getInstance("SHA1PRNG");
+            // Long 的最大值是 9223372036854775807,一共 19 位
+            // 以下主键,年只取最后2位,yyMMddHHmmssSSS 一共占了 15 位,后面还可以补 4 位随机数
+            // 最前面的年,在 Long 不溢出的情况下,可以用到 2092年12月31日,基本够用了
+            Long id = Long.parseLong(String.format("%s%04d",
+                    FastDateFormat.getInstance("yyMMddHHmmssSSS", Locale.US).format(date),
+                    number.nextInt(10000)));
+            return id;
+
+        } catch (Exception e) {
+            throw new RuntimeException("生成主键失败");
+        }
+    }
+
+
+}

+ 30 - 0
src/main/java/com/dtb/portal/util/RandomUtil.java

@@ -0,0 +1,30 @@
+package com.dtb.portal.util;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+/**
+ * @author fuzhiyou
+ * @date 2022-03-21 15:32
+ */
+public class RandomUtil {
+
+
+    /**
+     * @param num 生成的随机数多少位
+     * @return
+     */
+    public static String generateString(int num) {
+        return RandomStringUtils.random(num, true, true);
+    }
+
+    /**
+     * 生成num 随机数字
+     *
+     * @param num
+     * @return
+     */
+    public static String generateNumeric(int num) {
+        return RandomStringUtils.randomNumeric(num);
+    }
+
+}

+ 386 - 0
src/main/java/com/dtb/portal/util/SendSms.java

@@ -0,0 +1,386 @@
+package com.dtb.portal.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+//如果JDK版本是1.8,可使用原生Base64类
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+//如果JDK版本低于1.8,请使用三方库提供Base64类
+//import org.apache.commons.codec.binary.Base64;
+
+
+@Slf4j
+@Component
+public class SendSms {
+
+    //无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
+    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
+    //无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
+    private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
+
+
+    private static String appKey;
+    private static String appSecret;
+    private static String sender;
+    private static String templateId;
+    private static String url;
+
+    @Value("${sms.app-key}")
+    public void setAppKey(String appKey) {
+        this.appKey = appKey;
+    }
+
+    @Value("${sms.app-secret}")
+    public void setAppSecret(String appSecret) {
+        this.appSecret = appSecret;
+    }
+
+    @Value("${sms.sender}")
+    public void setSender(String sender) {
+        this.sender = sender;
+    }
+
+
+    @Value("${sms.template-id}")
+    public void setTemplateId(String templateId) {
+        this.templateId = templateId;
+    }
+
+    @Value("${sms.url}")
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    /**
+     * 发送短信
+     */
+    public static String sendSms(String phone) throws Exception {
+        //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
+        String signature = "验证码类"; //签名名称
+        //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
+        String receiver = "+86" + phone; //短信接收人号码
+        //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
+        String statusCallBack = "";
+
+        /**
+         * 选填,使用无变量模板时请赋空值 String templateParas = "";
+         * 单变量模板示例:模板内容为"您的验证码是${1}"时,templateParas可填写为"[\"369751\"]"
+         * 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]"
+         * 模板中的每个变量都必须赋值,且取值不能为空
+         * 查看更多模板规范和变量规范:产品介绍>短信模板须知和短信变量须知
+         */
+        String code = RandomUtil.generateNumeric(4);
+        log.info("发送的短信验证码是:{}", code);
+        String templateParas = "[\"" + code + "\"]"; //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
+        //请求Body,不携带签名名称时,signature请填null
+        String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature);
+        if (null == body || body.isEmpty()) {
+            System.out.println("body is null.");
+            throw new RuntimeException("body is null.");
+        }
+        //请求Headers中的X-WSSE参数值
+        String wsseHeader = buildWsseHeader(appKey, appSecret);
+        if (null == wsseHeader || wsseHeader.isEmpty()) {
+            throw new RuntimeException("wsse header is null.");
+        }
+
+        Writer out = null;
+        BufferedReader in = null;
+        StringBuffer result = new StringBuffer();
+        HttpsURLConnection connection = null;
+        InputStream is = null;
+        HostnameVerifier hv = (hostname, session) -> true;
+        trustAllHttpsCertificates();
+        try {
+            URL realUrl = new URL(url);
+            connection = (HttpsURLConnection) realUrl.openConnection();
+            connection.setHostnameVerifier(hv);
+            connection.setDoOutput(true);
+            connection.setDoInput(true);
+            connection.setUseCaches(true);
+            //请求方法
+            connection.setRequestMethod("POST");
+            //请求Headers参数
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE);
+            connection.setRequestProperty("X-WSSE", wsseHeader);
+            connection.connect();
+            out = new OutputStreamWriter(connection.getOutputStream());
+            out.write(body); //发送请求Body参数
+            out.flush();
+            out.close();
+
+            int status = connection.getResponseCode();
+            if (200 == status) { //200
+                is = connection.getInputStream();
+            } else { //400/401
+                is = connection.getErrorStream();
+            }
+            in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+            String line = "";
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            log.info("发送短信返回的消息体是:{}", result.toString());
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (null != out) {
+                    out.close();
+                }
+                if (null != is) {
+                    is.close();
+                }
+                if (null != in) {
+                    in.close();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return code;
+    }
+
+/*    public static void main(String[] args) throws Exception {
+
+        //必填,请参考"开发准备"获取如下数据,替换为实际值
+        String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1"; //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
+        // 认证用的appKey和appSecret硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
+        String appKey = "FiqQB99JNN37gfpn40x91Y7aMlrO"; //APP_Key
+        String appSecret = "RGgWWDsNNyhzy9yrhBRfQgpa2JI0"; //APP_Secret
+        String sender = "8823120508279"; //国内短信签名通道号
+        String templateId = "1a6b0f3609af4184b0832523835bf3fd"; //模板ID
+
+        //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
+
+        String signature = "验证码类"; //签名名称
+
+        //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
+        String receiver = "+8618289387963"; //短信接收人号码
+
+        //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
+        String statusCallBack = "";
+
+
+        String code1 = RandomUtil.generateNumeric(6);
+        String code2 = RandomUtil.generateNumeric(6);
+        System.out.println("发送的验证码是:" + code1);
+        //System.out.println("发送的验证码是:" + code1);
+        String templateParas = "[\""+code1+"\"]"; //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
+        //String templateParas = "[\"450088\",\"451100\"]";
+        //String templateParas = "[\""+code1+"\",\""+code2+"\"]";
+        log.info("发送的模板是:{}", templateParas);
+
+        //请求Body,不携带签名名称时,signature请填null
+        String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature);
+        if (null == body || body.isEmpty()) {
+            System.out.println("body is null.");
+            return;
+        }
+
+        //请求Headers中的X-WSSE参数值
+        String wsseHeader = buildWsseHeader(appKey, appSecret);
+        if (null == wsseHeader || wsseHeader.isEmpty()) {
+            System.out.println("wsse header is null.");
+            return;
+        }
+
+        Writer out = null;
+        BufferedReader in = null;
+        StringBuffer result = new StringBuffer();
+        HttpsURLConnection connection = null;
+        InputStream is = null;
+
+
+        HostnameVerifier hv = new HostnameVerifier() {
+
+            @Override
+            public boolean verify(String hostname, SSLSession session) {
+                return true;
+            }
+        };
+        trustAllHttpsCertificates();
+
+        try {
+            URL realUrl = new URL(url);
+            connection = (HttpsURLConnection) realUrl.openConnection();
+
+            connection.setHostnameVerifier(hv);
+            connection.setDoOutput(true);
+            connection.setDoInput(true);
+            connection.setUseCaches(true);
+            //请求方法
+            connection.setRequestMethod("POST");
+            //请求Headers参数
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE);
+            connection.setRequestProperty("X-WSSE", wsseHeader);
+
+            connection.connect();
+            out = new OutputStreamWriter(connection.getOutputStream());
+            out.write(body); //发送请求Body参数
+            out.flush();
+            out.close();
+
+            int status = connection.getResponseCode();
+            if (200 == status) { //200
+                is = connection.getInputStream();
+            } else { //400/401
+                is = connection.getErrorStream();
+            }
+            in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+            String line = "";
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            System.out.println(result.toString()); //打印响应消息实体
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (null != out) {
+                    out.close();
+                }
+                if (null != is) {
+                    is.close();
+                }
+                if (null != in) {
+                    in.close();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }*/
+
+    /**
+     * 构造请求Body体
+     *
+     * @param sender
+     * @param receiver
+     * @param templateId
+     * @param templateParas
+     * @param statusCallBack
+     * @param signature      | 签名名称,使用国内短信通用模板时填写
+     * @return
+     */
+    static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
+                                   String statusCallBack, String signature) {
+        if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
+                || templateId.isEmpty()) {
+            return null;
+        }
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("from", sender);
+        map.put("to", receiver);
+        map.put("templateId", templateId);
+        if (null != templateParas && !templateParas.isEmpty()) {
+            map.put("templateParas", templateParas);
+        }
+        if (null != statusCallBack && !statusCallBack.isEmpty()) {
+            map.put("statusCallback", statusCallBack);
+        }
+        if (null != signature && !signature.isEmpty()) {
+            map.put("signature", signature);
+        }
+
+        StringBuilder sb = new StringBuilder();
+        String temp = "";
+        for (String s : map.keySet()) {
+            try {
+                temp = URLEncoder.encode(map.get(s), "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+            sb.append(s).append("=").append(temp).append("&");
+        }
+
+        return sb.deleteCharAt(sb.length() - 1).toString();
+    }
+
+    /**
+     * 构造X-WSSE参数值
+     *
+     * @param appKey
+     * @param appSecret
+     * @return
+     */
+    static String buildWsseHeader(String appKey, String appSecret) {
+        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
+            System.out.println("buildWsseHeader(): appKey or appSecret is null.");
+            return null;
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+        String time = sdf.format(new Date()); //Created
+        String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce
+
+        MessageDigest md;
+        byte[] passwordDigest = null;
+
+        try {
+            md = MessageDigest.getInstance("SHA-256");
+            md.update((nonce + time + appSecret).getBytes());
+            passwordDigest = md.digest();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+
+        //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
+        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
+        //如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
+        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
+        //若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
+        //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
+        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
+    }
+
+    /*** @throws Exception
+     */
+    private static void trustAllHttpsCertificates() throws Exception {
+        TrustManager[] trustAllCerts = new TrustManager[]{
+                new X509TrustManager() {
+                    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                        return;
+                    }
+
+                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                        return;
+                    }
+
+                    public X509Certificate[] getAcceptedIssuers() {
+                        return null;
+                    }
+                }
+        };
+        SSLContext sc = SSLContext.getInstance("SSL");
+        sc.init(null, trustAllCerts, null);
+        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+    }
+}

+ 35 - 0
src/main/java/com/dtb/portal/util/StrengthUtil.java

@@ -0,0 +1,35 @@
+package com.dtb.portal.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.regex.Pattern;
+
+/**
+ * @author fuzhiyou
+ * @date 2022-04-08 19:06
+ */
+public class StrengthUtil {
+
+    /**
+     * 匹配密码必须包含大小写字母,数字,特殊字符,长度必须大于等于8位
+     */
+    final static String PATTERN = "^(?![A-Za-z0-9]+$)(?![a-z0-9\\W]+$)(?![A-Za-z\\W]+$)(?![A-Z0-9\\W]+$)[a-zA-Z0-9\\W]{8,}$";
+
+    public static int getStrength(String password) {
+        if (StringUtils.isNotEmpty(password)) {
+            if (8 > password.length()) {
+                return 0;
+            }
+            if (password.length() <= 15) {
+                return 1;
+            }
+            return 2;
+        }
+       throw new RuntimeException("密码为空");
+    }
+
+    public static boolean checkPwd(String password) {
+        return password.matches(PATTERN);
+
+    }
+}

+ 48 - 0
src/main/java/com/dtb/portal/util/TokenEnum.java

@@ -0,0 +1,48 @@
+
+package com.dtb.portal.util;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author xumj
+ * @date 2021-08-11
+ */
+@AllArgsConstructor
+@Getter
+public enum TokenEnum {
+
+    AUTH_HEADER_KEY("authorization", "Authorization"),
+
+    TOKEN_PREFIX("bearer", "Bearer "),
+
+    TOKEN_SECRET("secret", "qwertyuiopasdfghjklmnbvcxz!@#$%^1234567890"),
+
+    TOKEN_EXPIRE("expire", "3600"),
+
+    TOKEN_USER_ID("id", "token_user_id"),
+
+    TOKEN_USER_NAME("name", "token_user_name"),
+
+    TOKEN_USER_PASSWORD("password", "token_user_password"),
+
+    TOKEN_TYPE("type", "oauth"),
+
+    TOKEN_CREATE_TIME("time", "token_create_time"),
+
+    TOKEN_SESSION("session", "sessionId"),
+
+    AUTHING_HEADER_KEY("authing", "authentication"),
+
+    AUTHING_USER_KEY("authing-user", "authing-user"),
+
+    AUTHING_CLIENT_ID("authing-client-id", "6006944d9c5a52e735d3983d"),
+
+    AUTHING_TOKEN("authing-token", "authing-token"),
+
+    ;
+
+    private String code;
+    private String message;
+
+}

+ 113 - 0
src/main/java/com/dtb/portal/util/ValidateCodeImageUtils.java

@@ -0,0 +1,113 @@
+package com.dtb.portal.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.Base64;
+import java.util.Random;
+
+/**
+ * 验证码图片工具类
+ * @author xumj
+ * @date 2021-08-11
+ */
+@Slf4j
+public class ValidateCodeImageUtils {
+
+    private static Random random = new Random();
+    private static int width = 165; //验证码的宽
+    private static int height = 45; //验证码的高
+    private static int lineSize = 65; //验证码中夹杂的干扰线数量
+    private static int randomStrNum = 4; //验证码字符个数
+
+    private static String randomString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWSYZ";
+
+    // 字体的设置
+    private static Font getFont() {
+        return new Font("Times New Roman", Font.ROMAN_BASELINE, 40);
+    }
+
+    // 颜色的设置
+    private static Color getRandomColor(int fc, int bc) {
+        fc = Math.min(fc, 255);
+        bc = Math.min(bc, 255);
+        int r = fc + random.nextInt(bc - fc - 16);
+        int g = fc + random.nextInt(bc - fc - 14);
+        int b = fc + random.nextInt(bc - fc - 12);
+        return new Color(r, g, b);
+    }
+
+    // 干扰线的绘制
+    private static void drawLine(Graphics g) {
+        int x = random.nextInt(width);
+        int y = random.nextInt(height);
+        int xl = random.nextInt(20);
+        int yl = random.nextInt(10);
+        g.drawLine(x, y, x + xl, y + yl);
+    }
+
+    // 随机字符的获取
+    private static String getRandomString(int num){
+        num = num > 0 ? num : randomString.length();
+        return String.valueOf(randomString.charAt(random.nextInt(num)));
+    }
+
+    // 字符串的绘制
+    private static String drawString(Graphics g, String randomStr, int i) {
+        g.setFont(getFont());
+        g.setColor(getRandomColor(108, 190));
+        //System.out.println(random.nextInt(randomString.length()));
+        String rand = getRandomString(random.nextInt(randomString.length()));
+        randomStr += rand;
+        g.translate(random.nextInt(3), random.nextInt(6));
+        g.drawString(rand, 40 * i + 10, 25);
+        return randomStr;
+    }
+
+    // 生成随机图片并转base64字符串
+    public static String getRandomCodeImage() {
+        String base64String = "";
+        // BufferedImage类是具有缓冲区的Image类, Image类是用于描述图像信息的类
+        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
+        Graphics g = image.getGraphics();
+        g.fillRect(0, 0, width, height);
+        g.setColor(getRandomColor(105, 189));
+        g.setFont(getFont());
+        // 干扰线
+        for (int i = 0; i < lineSize; i++) {
+            drawLine(g);
+        }
+        // 随机字符
+        String randomStr = "";
+        for (int i = 0; i < randomStrNum; i++) {
+            randomStr = drawString(g, randomStr, i);
+        }
+        log.info("验证码 :{}", randomStr);
+        g.dispose();
+
+        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
+        try {
+            //  将图片以png格式返回,返回的是图片
+            ImageIO.write(image, "gif", jpegOutputStream);
+            byte[] bytes = jpegOutputStream.toByteArray();
+            Base64.Encoder encoder = Base64.getEncoder();
+            base64String = encoder.encodeToString(bytes);
+        } catch (Exception e) {
+            throw new RuntimeException(CodeDefault.CODE_CREATE_ERROR.getDefaultMessage());// 验证生成异常
+        } finally {
+            try {
+                jpegOutputStream.flush();
+                jpegOutputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return randomStr + "," + base64String;
+    }
+
+
+}

+ 162 - 0
src/main/java/com/dtb/portal/util/WxAppApiUtils.java

@@ -0,0 +1,162 @@
+package com.dtb.portal.util;
+
+import com.dtb.portal.constans.WxAppApiConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.AbstractMinValidator;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Map;
+
+
+@Slf4j
+public class WxAppApiUtils {
+
+    public static String getOpenId(String appId, String appSecret, String code) {
+        String url = WxAppApiConstant.CODE_TO_SESSION.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);
+        log.info("访问的路径是:{}", url);
+        String result = sendGet(url, null);
+        log.info("wx_app_api request open_id result : " + result);
+        return result;
+    }
+
+    /**
+     * 小程序获取openid和session_key
+     * @param appId 小程序appid
+     * @param appSecret 小程序密钥
+     * @param code 小程序code
+     * @return 包含openid和session_key的JSON字符串
+     */
+    public static String getMiniprogramOpenId(String appId, String appSecret, String code) {
+        String url = WxAppApiConstant.CODE_TO_SESSION.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);
+        log.info("小程序获取openid请求路径:{}", url);
+        String result = sendGet(url, null);
+        log.info("小程序获取openid返回结果: {}", result);
+        return result;
+    }
+
+    public static String sendGet(String url, Map<String, String> parameters) {
+        String result = "";
+        BufferedReader in = null;// 读取响应输入流
+        StringBuffer sb = new StringBuffer();// 存储参数
+
+        try {
+            if (parameters != null) {
+                // 编码请求参数
+                for (Map.Entry<String, String> entry : parameters.entrySet()) {
+                    sb.append(entry.getKey())
+                            .append('=')
+                            .append(URLEncoder.encode(entry.getValue(), "UTF-8").toString())
+                            .append('&');
+                }
+
+                url += "?" + sb.substring(0, sb.length() - 1);
+            }
+
+            // 创建URL对象
+            URL connURL = new URL(url);
+            // 打开URL连接
+            HttpURLConnection httpConn = (HttpURLConnection) connURL.openConnection();
+            // 设置通用属性
+            httpConn.setRequestProperty("Accept", "*/*");
+            httpConn.setRequestProperty("Connection", "Keep-Alive");
+            httpConn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
+            // 建立实际的连接
+            httpConn.connect();
+            /*
+            // 响应头部获取
+            Map<String, List<String>> headers = httpConn.getHeaderFields();
+            // 遍历所有的响应头字段
+            for (String key : headers.keySet()) {
+                System.out.println(key + "\t:\t" + headers.get(key));
+            }
+            */
+            // 定义BufferedReader输入流来读取URL的响应,并设置编码方式
+            in = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
+            String line;
+            // 读取返回的内容
+            while ((line = in.readLine()) != null) {
+                result += line;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    public static String sendPost(String url, String jsonData) {
+        String result = "";
+        BufferedReader in = null;
+        OutputStream out = null;
+
+        try {
+            // 创建URL对象
+            URL connURL = new URL(url);
+            // 打开URL连接
+            HttpURLConnection httpConn = (HttpURLConnection) connURL.openConnection();
+            // 设置请求方法为POST
+            httpConn.setRequestMethod("POST");
+            // 设置通用属性
+            httpConn.setRequestProperty("Accept", "*/*");
+            httpConn.setRequestProperty("Connection", "Keep-Alive");
+            httpConn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
+            httpConn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+            // 允许输出
+            httpConn.setDoOutput(true);
+            // 允许输入
+            httpConn.setDoInput(true);
+            
+            // 建立实际的连接
+            httpConn.connect();
+            
+            // 发送数据
+            if (jsonData != null && !jsonData.isEmpty()) {
+                out = httpConn.getOutputStream();
+                out.write(jsonData.getBytes("UTF-8"));
+                out.flush();
+            }
+            
+            // 定义BufferedReader输入流来读取URL的响应,并设置编码方式
+            in = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
+            String line;
+            // 读取返回的内容
+            while ((line = in.readLine()) != null) {
+                result += line;
+            }
+        } catch (Exception e) {
+            log.error("发送POST请求失败", e);
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+                if (out != null) {
+                    out.close();
+                }
+            } catch (IOException ex) {
+                log.error("关闭流失败", ex);
+            }
+        }
+        return result;
+    }
+
+    public static void main(String[] args) {
+        String appId = "wxc7741d3f2470da35";
+        String appSecret = "1c3d8138410a3e326146398092387564";
+        String code = "0039v7000FAJeR1ZLJ200Z3dmI09v70c";
+        getOpenId(appId, appSecret, code);
+    }
+}

+ 210 - 0
src/main/java/com/dtb/portal/util/WxPhoneNumberUtils.java

@@ -0,0 +1,210 @@
+package com.dtb.portal.util;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+@Slf4j
+public class WxPhoneNumberUtils {
+
+    /**
+     * 获取微信手机号和openId
+     */
+    public static JSONObject getPhoneNumberAndOpenId(String appId, String appSecret, String phoneCode, String loginCode) {
+        try {
+            // 1. 通过登录code获取session_key和openid
+            String url = String.format(
+                "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
+                appId, appSecret, loginCode);
+            
+            JSONObject sessionResult = httpGet(url);
+            String sessionKey = sessionResult.getString("session_key");
+            String openId = sessionResult.getString("openid");
+            String unionId = sessionResult.getString("unionid");
+            
+            if (StringUtils.isBlank(sessionKey)) {
+                throw new RuntimeException("获取session_key失败");
+            }
+            
+            // 2. 通过手机号code获取手机号
+            url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
+            String accessToken = getAccessToken(appId, appSecret);
+            url = url + "?access_token=" + accessToken;
+            
+            // 构建请求体
+            JSONObject requestBody = new JSONObject();
+            requestBody.put("code", phoneCode);
+            
+            // 发送POST请求
+            JSONObject phoneResult = httpPost(url, requestBody);
+            log.info("获取手机号结果: {}", phoneResult);
+            
+            if (phoneResult.getInteger("errcode") != 0) {
+                throw new RuntimeException("获取手机号失败: " + phoneResult.getString("errmsg"));
+            }
+            
+            String phoneNumber = phoneResult.getJSONObject("phone_info").getString("phoneNumber");
+            
+            // 3. 返回结果
+            JSONObject result = new JSONObject();
+            result.put("phoneNumber", phoneNumber);
+            result.put("openId", openId);
+            result.put("unionId", unionId);
+            return result;
+            
+        } catch (Exception e) {
+            log.error("获取微信手机号失败", e);
+            throw new RuntimeException("获取微信手机号失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 获取APP微信登录的openId
+     */
+    public static JSONObject getOpenIdByCode(String appId, String appSecret, String code) {
+        try {
+            // 通过code获取access_token和openid
+            String url = String.format(
+                "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
+                appId, appSecret, code);
+            
+            log.info("请求微信OAuth2接口URL: {}", url);
+            JSONObject result = httpGet(url);
+            log.info("微信OAuth2接口返回: {}", result);
+            
+            // 如果返回错误,尝试使用替代接口
+            if (result.containsKey("errcode") && result.getIntValue("errcode") != 0) {
+                // 尝试使用替代接口
+                url = String.format(
+                    "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
+                    appId, appSecret, code);
+                    
+                log.info("尝试替代接口URL: {}", url);
+                result = httpGet(url);
+                log.info("替代接口返回: {}", result);
+            }
+            
+            // 检查错误码
+            if (result.containsKey("errcode") && result.getIntValue("errcode") != 0) {
+                throw new RuntimeException("微信接口返回错误: " + result.getString("errmsg"));
+            }
+            
+            String accessToken = result.getString("access_token");
+            String openId = result.getString("openid");
+            String unionId = result.getString("unionid");
+            
+            if (StringUtils.isBlank(openId)) {
+                throw new RuntimeException("获取openid失败,微信接口返回: " + result.toJSONString());
+            }
+            
+            // 返回结果
+            JSONObject response = new JSONObject();
+            response.put("openid", openId);
+            response.put("unionid", unionId);
+            response.put("access_token", accessToken);
+            return response;
+            
+        } catch (Exception e) {
+            log.error("获取APP微信openId失败", e);
+            throw new RuntimeException("获取APP微信openId失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 获取access_token
+     */
+    private static String getAccessToken(String appId, String appSecret) {
+        String url = String.format(
+            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
+            appId, appSecret);
+            
+        JSONObject result = httpGet(url);
+        return result.getString("access_token");
+    }
+    
+    /**
+     * 发送GET请求
+     */
+    public static JSONObject httpGet(String url) {
+        try {
+            CloseableHttpClient client = HttpClients.createDefault();
+            HttpGet httpGet = new HttpGet(url);
+            CloseableHttpResponse response = client.execute(httpGet);
+            HttpEntity entity = response.getEntity();
+            String result = EntityUtils.toString(entity);
+            JSONObject jsonResult = JSONObject.parseObject(result);
+            log.debug("HTTP GET Response: {}", jsonResult);
+            return jsonResult;
+        } catch (Exception e) {
+            log.error("HTTP GET请求失败: {}", e.getMessage());
+            throw new RuntimeException("HTTP请求失败: " + e.getMessage());
+        }
+    }
+    
+    private static JSONObject httpPost(String url, JSONObject body) {
+        try {
+            CloseableHttpClient client = HttpClients.createDefault();
+            HttpPost httpPost = new HttpPost(url);
+            
+            // 设置请求头
+            httpPost.setHeader("Content-Type", "application/json");
+            
+            // 设置请求体
+            StringEntity entity = new StringEntity(body.toString(), "UTF-8");
+            httpPost.setEntity(entity);
+            
+            // 发送请求
+            CloseableHttpResponse response = client.execute(httpPost);
+            HttpEntity responseEntity = response.getEntity();
+            String result = EntityUtils.toString(responseEntity);
+            
+            JSONObject jsonResult = JSONObject.parseObject(result);
+            log.debug("HTTP POST Response: {}", jsonResult);
+            return jsonResult;
+        } catch (Exception e) {
+            log.error("HTTP POST请求失败: {}", e.getMessage());
+            throw new RuntimeException("HTTP请求失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 调试微信code
+     */
+    public static JSONObject debugCode(String appId, String appSecret, String code) {
+        JSONObject debug = new JSONObject();
+        try {
+            // 获取session_key和openid
+            String url = String.format(
+                "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
+                appId, appSecret, code);
+            
+            JSONObject sessionResult = httpGet(url);
+            debug.put("jscode2session", sessionResult);
+            
+            // 获取access_token
+            String accessToken = getAccessToken(appId, appSecret);
+            debug.put("access_token", accessToken);
+            
+            // 获取手机号
+            url = String.format(
+                "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s",
+                accessToken);
+                
+            JSONObject phoneResult = httpGet(url);
+            debug.put("phone_result", phoneResult);
+            
+            return debug;
+        } catch (Exception e) {
+            debug.put("error", e.getMessage());
+            return debug;
+        }
+    }
+}

+ 129 - 0
src/main/resources/application.properties

@@ -0,0 +1,129 @@
+# 端口及上下文
+server.port=18885
+server.servlet.session.timeout=PT2H
+server.servlet.context-path=/api
+server.servlet.servlet-path=/api
+server.max-http-header-size=8096
+server.tomcat.max-swallow-size=100MB
+server.tomcat.max-http-form-post-size=100MB
+
+# 文件上传
+spring.servlet.multipart.max-request-size=256MB
+spring.servlet.multipart.max-file-size=256MB
+
+# 数据源
+spring.datasource.initialSize=5
+spring.datasource.minIdle=5
+spring.datasource.maxActive=20
+spring.datasource.maxWait=60000
+spring.datasource.timeBetweenEvictionRunsMillis=60000
+spring.datasource.minEvictableIdleTimeMillis=300000
+spring.datasource.validationQuery=SELECT 1
+spring.datasource.testWhileIdle=true
+spring.datasource.testOnBorrow=false
+spring.datasource.testOnReturn=false
+spring.datasource.poolPreparedStatements=true
+spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
+spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
+spring.datasource.username=user_pay
+spring.datasource.password=SByKpfSdZWhSWdwi
+spring.datasource.url=jdbc:mysql://47.115.208.13:3306/user_pay?characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai
+spring.session.store-type=redis
+spring.application.name=S-DaaS-partal
+# spring.redis.host=1.94.204.179
+# spring.redis.host=127.0.0.1
+# spring.redis.port=6379
+# spring.redis.password=Qwerty9527
+spring.redis.host=8.155.53.54
+spring.redis.port=6379
+spring.redis.password=abc123456abc-test
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=Asia/Shanghai
+mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+
+# 日志
+logging.level.com.dtb.portal.mapper=trace
+logging.level.root=debug
+
+# 上传文件路径前缀
+file.url=https://fun9527.com
+
+# 微信小程序配置
+wechat.miniprogram.app_id=wxfbd4d494bac61371
+wechat.miniprogram.app_secret=7c77d21897eb0b59b31db785100c566c
+
+# app微信配置
+wechat.app.app_id=wxc7741d3f2470da35
+wechat.app.app_secret=1c3d8138410a3e326146398092387564
+
+# 短信配置
+sms.app-key=FiqQB99JNN37gfpn40x91Y7aMlrO
+sms.app-secret=RGgWWDsNNyhzy9yrhBRfQgpa2JI0
+sms.sender=8823120508279
+sms.template-id=1a6b0f3609af4184b0832523835bf3fd
+sms.url=https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1
+
+# 服务器域名
+service.domain=http://127.0.0.1:8080
+
+# 微信支付相关 - APP支付配置(保留备用)
+wxpay.app.appId=wxfbd4d494bac61371
+wxpay.app.mchId=1724190145
+wxpay.app.key=7c77d21897eb0b59b31db785100c566c
+wxpay.app.certPath=/www/wwwroot/fun9527.com/apiclient_cert.p12
+wxpay.app.payNotifyUrl=http://47.115.208.13:18885/api/wxPay/notify
+wxpay.app.refundNotifyUrl=http://47.115.208.13:18885/api/wxPay/refundNotify
+wxpay.app.merchantSerialNumber=3D8D97E7A4958AA74D37593D94CD276BA4DD6896
+wxpay.app.v3Key=K8pLmQ2sR7tU4vX6wZ9jH3gF5dS1aB0c
+
+# 微信支付相关 - 小程序支付配置
+wxpay.miniprogram.appId=wxfbd4d494bac61371
+wxpay.miniprogram.mchId=1724190145
+wxpay.miniprogram.key=7c77d21897eb0b59b31db785100c566c
+wxpay.miniprogram.certPath=/www/wwwroot/fun9527.com/apiclient_key.pem
+wxpay.miniprogram.payNotifyUrl=http://47.115.208.13:18885/api/wxPay/miniprogram/notify
+wxpay.miniprogram.refundNotifyUrl=http://47.115.208.13:18885/api/wxPay/miniprogram/refundNotify
+wxpay.miniprogram.merchantSerialNumber=3D8D97E7A4958AA74D37593D94CD276BA4DD6896
+wxpay.miniprogram.v3Key=K8pLmQ2sR7tU4vX6wZ9jH3gF5dS1aB0c
+
+# 安全配置
+dtb.security.secret=otherpeopledontknowit
+dtb.security.prefix=Bearer
+dtb.security.header=Authorization
+dtb.security.loginUrl=/login
+dtb.security.expiration=86400
+dtb.security.redisExpire=7200 
+
+# 阿里云配置
+# 公共配置
+aliyun.accessKeyId=LTAI4FxD7T4x5BSxoQvbYkdR
+aliyun.AccessKeySecret=FZRhkmzXJ1lwcosNszVHr5VLW0lxic
+
+# OSS服务配置
+# 是否使用自有域名,不使用自有域名的话,默认endpoint
+aliyun.usingDomain=false
+aliyun.endpoint=oss-cn-beijing.aliyuncs.com
+aliyun.domainName=http://yuyekeji.cn
+# 分环境,创建
+aliyun.bucketName=susuan-bucket
+# 默认存储地方
+aliyun.fileHost=host
+# 头像文件夹名字
+aliyun.fileAvatar=avatar
+# 公共头像文件夹
+aliyun.filePublicAvatar=publicAvatar
+# 游戏icon图片
+aliyun.fileGameIcon=gameIcon
+# 其他图库文件夹
+aliyun.filePictures=pictures
+# 视频文件夹
+aliyun.fileVideos=videos
+# 图片文件夹
+aliyun.fileImages=images
+
+# 短信服务配置
+aliyun.regionId=cn-hangzhou
+aliyun.signName=\u54d4\u8e66\u6e38\u620f\u51cf\u8102\u5e73\u53f0
+aliyun.templateCode=SMS_183200249 

+ 145 - 0
src/main/resources/mybatis/SysUserMapper.xml

@@ -0,0 +1,145 @@
+<?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.dtb.portal.mapper.SysUserMapper">
+    <select id="page" resultType="com.dtb.portal.entity.SysUser">
+        select *
+        from users where 1=1
+        <if test="accountName!=null and accountName!=''">
+            and account_name like CONCAT('%',#{accountName,jdbcType=VARCHAR},'%')
+        </if>
+
+        <if test="phone!=null and phone!=''">
+            and phone like CONCAT('%',#{phone,jdbcType=VARCHAR,jdbcType=VARCHAR},'%')
+        </if>
+        <if test="enable!=null and enable!=''">
+            and enable =#{enable,jdbcType=VARCHAR}
+        </if>
+        and type='2'
+        order by create_time desc
+    </select>
+
+    <select id="selectOne" resultType="com.dtb.portal.entity.SysUser">
+        select *
+        from users
+        where id = #{id,jdbcType=VARCHAR}
+    </select>
+
+    <update id="updateEnable">
+        update users
+        <set>
+            <if test="enable!=null">
+                enable =#{enable},
+            </if>
+            <if test="updateTime!=null">
+                update_time =#{updateTime,jdbcType=TIMESTAMP},
+            </if>
+        </set>
+        where id=#{id,jdbcType=VARCHAR}
+    </update>
+
+    <update id="updatePassword">
+        update users
+        <set>
+            <if test="password!=null and password!=''">
+                password=#{password,jdbcType=VARCHAR},
+            </if>
+            <if test="updateTime!=null">
+                update_time =#{updateTime,jdbcType=TIMESTAMP},
+            </if>
+        </set>
+        where id=#{id,jdbcType=VARCHAR}
+    </update>
+
+    <select id="selectByAccountName" resultType="com.dtb.portal.entity.SysUser">
+        select *
+        from users
+        where account_name = #{accountName,jdbcType=VARCHAR}
+    </select>
+
+    <select id="selectByOpenId" resultType="com.dtb.portal.entity.SysUser">
+        select *
+        from users
+        where open_id = #{openId,jdbcType=VARCHAR}
+    </select>
+
+    <update id="update">
+        UPDATE users
+        <set>
+            <if test="accountName!=null and accountName!=''">
+                account_name=#{accountName,jdbcType=VARCHAR},
+            </if>
+            <if test="nickname!=null and nickname!=''">
+                nickname=#{nickname,jdbcType=VARCHAR},
+            </if>
+            <if test="password!=null and password!=''">
+                password=#{password,jdbcType=VARCHAR},
+            </if>
+            <if test="phone!=null and phone!=''">
+                phone=#{phone,jdbcType=VARCHAR},
+            </if>
+            <if test="enable!=null">
+                enable=#{enable},
+            </if>
+            <if test="gender!=null and gender!=''">
+                gender=#{gender,jdbcType=VARCHAR},
+            </if>
+            <if test="location!=null and location!=''">
+                location=#{location,jdbcType=VARCHAR},
+            </if>
+            <if test="nation!=null and nation!=''">
+                nation=#{nation,jdbcType=VARCHAR},
+            </if>
+            <if test="birthday!=null">
+                birthday=#{birthday,jdbcType=TIMESTAMP},
+            </if>
+            <if test="updateTime!=null">
+                update_time=#{updateTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="isVip!=null">
+                is_vip=#{isVip},
+            </if>
+            <if test="vipResidueDate!=null">
+                vip_residue_date=#{vipResidueDate},
+            </if>
+            <if test="vipEndDate!=null">
+                vip_end_date=#{vipEndDate},
+            </if>
+            logo_path=#{logoPath,jdbcType=VARCHAR},
+        </set>
+        where id=#{id,jdbcType=VARCHAR}
+    </update>
+
+    <insert id="save">
+        insert into users (id, open_id, type, logo_path, account_name, nickname, password, phone, enable, gender,
+                           location,nation, birthday, create_time, update_time)
+        values (#{id,jdbcType=VARCHAR},
+                #{openId,jdbcType=VARCHAR},
+                #{type,jdbcType=VARCHAR},
+                #{logoPath,jdbcType=VARCHAR},
+                #{accountName,jdbcType=VARCHAR},
+                #{nickname,jdbcType=VARCHAR},
+                #{password,jdbcType=VARCHAR},
+                #{phone,jdbcType=VARCHAR},
+                #{enable},
+                #{gender,jdbcType=VARCHAR},
+                #{location,jdbcType=VARCHAR},
+                #{nation,jdbcType=VARCHAR},
+                #{birthday,jdbcType=TIMESTAMP},
+                #{createTime,jdbcType=TIMESTAMP},
+                #{updateTime,jdbcType=TIMESTAMP})
+    </insert>
+
+    <select id="selectByPhone" resultType="com.dtb.portal.entity.SysUser">
+        select *
+        from users
+        where phone = #{phone,jdbcType=VARCHAR}
+          and type = '2'
+    </select>
+
+    <delete id="delete">
+        delete
+        from users
+        where id = #{id,jdbcType=VARCHAR}
+    </delete>
+</mapper>

+ 69 - 0
src/main/resources/mybatis/VideoMapper.xml

@@ -0,0 +1,69 @@
+<?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.dtb.portal.mapper.VideoMapper">
+    <select id="page" resultType="com.dtb.portal.entity.Video">
+        select *
+        from users where 1=1
+        <if test="keyWord!=null and keyWord!=''">
+            and title like CONCAT('%',#{keyWord,jdbcType=VARCHAR},'%')
+            OR subtitle like CONCAT('%',#{keyWord,jdbcType=VARCHAR},'%')
+            OR content like CONCAT('%',#{keyWord,jdbcType=VARCHAR},'%')
+        </if>
+        order by create_time desc
+    </select>
+
+    <select id="selectOne" resultType="com.dtb.portal.entity.Video">
+        select *
+        from t_video_user
+        where id = #{id,jdbcType=VARCHAR}
+    </select>
+
+    <update id="updateEnable">
+        update t_video_user
+        <set>
+            <if test="title!=null and title!=''">
+                title=#{title,jdbcType=VARCHAR},
+            </if>
+            <if test="subtitle!=null and subtitle!=''">
+                subtitle=#{subtitle,jdbcType=VARCHAR},
+            </if>
+            <if test="num!=null and num!=''">
+                num=#{num,jdbcType=VARCHAR},
+            </if>
+            <if test="video_path!=null and video_path!=''">
+                video_path=#{video_path,jdbcType=VARCHAR},
+            </if>
+            <if test="video_img!=null and video_img!=''">
+                video_img=#{video_img,jdbcType=VARCHAR},
+            </if>
+            <if test="content!=null and content!=''">
+                content=#{content,jdbcType=VARCHAR},
+            </if>
+            <if test="updateTime!=null">
+                update_time =#{updateTime,jdbcType=TIMESTAMP},
+            </if>
+        </set>
+        where id=#{id,jdbcType=VARCHAR}
+    </update>
+
+    <insert id="save" useGeneratedKeys="true" keyProperty="id">
+        insert into t_video_user (title, subtitle, num, video_path, video_img, password, content, create_time, update_time)
+        values (
+                #{title,jdbcType=VARCHAR},
+                #{subtitle,jdbcType=VARCHAR},
+                #{num,jdbcType=VARCHAR},
+                #{video_path,jdbcType=VARCHAR},
+                #{nickname,jdbcType=VARCHAR},
+                #{video_img,jdbcType=VARCHAR},
+                #{content,jdbcType=VARCHAR},
+                #{createTime,jdbcType=TIMESTAMP},
+                #{updateTime,jdbcType=TIMESTAMP})
+    </insert>
+
+    <delete id="delete">
+        delete
+        from t_video_user
+        where id = #{id,jdbcType=VARCHAR}
+    </delete>
+</mapper>

+ 47 - 0
src/main/resources/mybatis/VideoUserMapper.xml

@@ -0,0 +1,47 @@
+<?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.dtb.portal.mapper.VideoUserMapper">
+    <resultMap id="BaseResultMap" type="com.dtb.portal.entity.VideoUser">
+        <id property="id" column="id" />
+        <result property="title" column="title" />
+        <result property="subtitle" column="subtitle" />
+        <result property="num" column="num" />
+        <result property="videoPath" column="video_path" />
+        <result property="videoImg" column="video_img" />
+        <result property="content" column="content" />
+        <result property="updateTime" column="update_time" />
+        <result property="createTime" column="create_time" />
+    </resultMap>
+
+    <insert id="insert" parameterType="com.dtb.portal.entity.VideoUser">
+        INSERT INTO t_video_user (id, title, subtitle, num, video_path, video_img, content, update_time, create_time)
+        VALUES (#{id}, #{title}, #{subtitle}, #{num}, #{videoPath}, #{videoImg}, #{content}, #{updateTime}, #{createTime})
+    </insert>
+
+    <update id="update" parameterType="com.dtb.portal.entity.VideoUser">
+        UPDATE t_video_user
+        SET title=#{title}, subtitle=#{subtitle}, num=#{num}, video_path=#{videoPath}, video_img=#{videoImg}, content=#{content}, update_time=#{updateTime}
+        WHERE id=#{id}
+    </update>
+
+    <delete id="deleteById" parameterType="string">
+        DELETE FROM t_video_user WHERE id=#{id}
+    </delete>
+
+    <select id="selectById" parameterType="string" resultMap="BaseResultMap">
+        SELECT * FROM t_video_user WHERE id=#{id}
+    </select>
+
+    <select id="selectList" resultMap="BaseResultMap">
+        SELECT * FROM t_video_user
+        <where>
+            <if test="title != null and title != ''">
+                AND title LIKE CONCAT('%', #{title}, '%')
+            </if>
+            <if test="content != null and content != ''">
+                AND content LIKE CONCAT('%', #{content}, '%')
+            </if>
+        </where>
+        ORDER BY create_time DESC
+    </select>
+</mapper> 

+ 123 - 0
src/main/resources/mybatis/VipGoodsMapper.xml

@@ -0,0 +1,123 @@
+<?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.dtb.portal.mapper.VipGoodsMapper">
+    <insert id="insert">
+        insert into t_vpi_goods(id, name, pay_mode, privilege, vip_date, vip_money, create_time, update_time)
+        values (#{id}, #{name}, #{payMode}, #{privilege}, #{vipDate}, #{vipMoney}, #{createTime}, #{updateTime})
+    </insert>
+
+    <select id="selectByName" resultType="com.dtb.portal.entity.VipGoodsData">
+        select *
+        from t_vpi_goods
+        where 1=1
+        <if test="name!=null and name!=''">
+            and name = #{name,jdbcType=VARCHAR}
+        </if>
+        <if test="id!=null and id!=''">
+            and id!=#{id,jdbcType=VARCHAR}
+        </if>
+    </select>
+
+    <update id="update">
+        update t_vpi_goods
+        <set>
+            <if test="name!=null and name!=''">
+                name=#{name,jdbcType=VARCHAR},
+            </if>
+            <if test="payMode!=null and payMode!=''">
+                pay_mode=#{payMode,jdbcType=VARCHAR},
+            </if>
+            <if test="privilege!=null and privilege!=''">
+                privilege=#{privilege,jdbcType=VARCHAR},
+            </if>
+            <if test="vipDate!=null and vipDate!=''">
+                vip_date=#{vipDate,jdbcType=VARCHAR},
+            </if>
+            <if test="vipDate!=null and vipDate!=''">
+                vip_money=#{vipMoney,jdbcType=VARCHAR},
+            </if>
+            <if test="updateTime!=null">
+                update_time=#{updateTime,jdbcType=VARCHAR},
+            </if>
+        </set>
+        where id=#{id,jdbcType=VARCHAR}
+    </update>
+
+    <delete id="delete">
+        delete
+        from t_vpi_goods
+        where id = #{id,jdbcType=VARCHAR}
+    </delete>
+
+    <select id="selectById" resultType="com.dtb.portal.entity.VipGoodsData">
+        select *
+        from t_vpi_goods
+        where id = #{id}
+    </select>
+
+    <select id="selectPage" resultType="com.dtb.portal.entity.VipGoodsData">
+        select * from t_vpi_goods where 1=1
+        <if test="name!=null and name!=''">
+            and name like CONCAT('%',#{name,jdbcType=VARCHAR},'%')
+        </if>
+        <if test="payMode!=null and payMode!=''">
+            and pay_mode=#{payMode,jdbcType=VARCHAR}
+        </if>
+        <if test="privilege!=null and privilege!=''">
+            and privilege=#{privilege,jdbcType=VARCHAR}
+        </if>
+        <if test="vipDate!=null and vipDate!=''">
+            and vip_date=#{vipDate,jdbcType=VARCHAR}
+        </if>
+        <if test="vipDate!=null and vipDate!=''">
+            and vip_money=#{vipMoney,jdbcType=VARCHAR}
+        </if>
+        order by create_time desc
+    </select>
+
+    <select id="selectByUserId" resultType="com.dtb.portal.entity.VipUserData">
+        select *
+        from t_vpi_user
+        where user_id = #{userId,jdbcType=VARCHAR} and vip_goods_id=#{vipGoodsId}
+
+        order by t_vpi_user.create_time desc limit 1
+    </select>
+
+    <insert id="insertVipUser">
+        insert into t_vpi_user (id, user_id, vip_goods_id, vip_start_date, vip_end_date, vip_residue_date, create_time,
+                                update_time)
+        values (#{id}, #{userId}, #{vipGoodsId}, #{vipStartDate}, #{vipEndDate}, #{vipResidueDate}, #{createTime},
+                #{updateTime})
+    </insert>
+
+    <delete id="deleteVipUser">
+        delete
+        from t_vpi_user
+        where id = #{id}
+    </delete>
+
+    <insert id="insertVipOrderData">
+        insert into t_vip_order(id, out_trade_no, vip_goods_id, user_id, openid, status, create_time, update_time)
+        values (#{id}, #{outTradeNo}, #{vipGoodsId}, #{userId}, #{openid}, #{status}, #{createTime}, #{updateTime})
+    </insert>
+
+    <select id="selectByOutTradeNo" resultType="com.dtb.portal.entity.VipOrderData">
+        select *
+        from t_vip_order
+        where out_trade_no = #{outTradeNo}
+    </select>
+
+    <update id="updateVipOrderData">
+        update t_vip_order
+        <set>
+            <if test="status!=null and status!=''">
+                status=#{status,jdbcType=VARCHAR},
+            </if>
+            <if test="updateTime!=null">
+                update_time=#{updateTime,jdbcType=VARCHAR},
+            </if>
+        </set>
+        where id=#{id,jdbcType=VARCHAR}
+    </update>
+</mapper>

+ 21 - 0
src/main/resources/vip表设计

@@ -0,0 +1,21 @@
+t_vpi_goods   vip商品表
+字段             字段注释
+id              主键
+name            商品名称
+pay_mode        支付方式 1:微信 2:其他
+privilege       svip特权
+vip_date        会员时长
+vip_money       会员金额
+create_time     创建时间
+update_time     更新时间
+
+t_vpi_user      vip用户
+字段             字段注释
+id              主键
+user_id         用户主键
+vip_goods_id    vip商品id
+vip_start_date  开通时间
+vip_end_date    结束时间
+vip_residue_date  剩余天数
+create_time     创建时间
+update_time     更新时间

+ 13 - 0
src/main/resources/需求

@@ -0,0 +1,13 @@
+管理端:
+用户名密码登陆,修改密码,用户禁用,用户列表
+
+手机端
+手机端:手机号登陆,微信登陆,微信注册,修改用户,上传头像
+
+
+需求
+1.vip商品管理(增删查改)
+2.微信支付
+3.vip会员有效期管理
+
+注:需要微信支付开发者账号

+ 13 - 0
src/test/java/com/dtb/portal/sdaasportal/ApplicationTests.java

@@ -0,0 +1,13 @@
+package com.dtb.portal.sdaasportal;
+
+
+
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Map;
+
+@SpringBootTest
+class SdaasportalApplicationTests {
+
+
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott