第一次上传
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Java files
|
||||
[*.java]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# XML files
|
||||
[*.xml]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# YAML files
|
||||
[*.{yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Properties files
|
||||
[*.properties]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Markdown files
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
+18
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="youlai-boot" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="youlai-boot" options="-parameters" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+20
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="http://maven.aliyun.com/nexus/content/groups/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2023-present 有来开源
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,69 @@
|
||||
# 创建一个名为 "youlai-boot" 的桥接网络,在同一个网络中的容器可以通过容器名互相访问
|
||||
networks:
|
||||
youlai-boot:
|
||||
driver: bridge
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0.29
|
||||
container_name: mysql
|
||||
restart: unless-stopped # 重启策略:除非手动停止容器,否则自动重启
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- LANG= en_US.UTF-8
|
||||
- MYSQL_ROOT_PASSWORD=123456 #设置 root 用户的密码
|
||||
volumes:
|
||||
- ./mysql/conf/my.cnf:/etc/my.cnf # 挂载 my.cnf 文件到容器的指定路径
|
||||
- ./mysql/data:/var/lib/mysql # 持久化 MySQL 数据
|
||||
- ../sql/mysql:/docker-entrypoint-initdb.d # 初始化 SQL 脚本目录
|
||||
ports:
|
||||
- 3306:3306
|
||||
networks:
|
||||
- youlai-boot # 加入 "youlai-boot" 网络
|
||||
|
||||
redis:
|
||||
image: redis:7.2.3
|
||||
container_name: redis
|
||||
restart: unless-stopped
|
||||
command: redis-server /etc/redis/redis.conf --requirepass 123456 --appendonly no # 启动 Redis 服务并添加密码为:123456,默认不开启 Redis AOF 方式持久化配置
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
- ./redis/config/redis.conf:/etc/redis/redis.conf
|
||||
ports:
|
||||
- 6379:6379
|
||||
networks:
|
||||
- youlai-boot
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2024-07-16T23-46-41Z
|
||||
container_name: minio
|
||||
restart: unless-stopped
|
||||
command: server /data --console-address ":9001"
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- LANG=en_US.UTF-8
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./minio/data:/data
|
||||
- ./minio/config:/root/.minio
|
||||
networks:
|
||||
- youlai-boot
|
||||
|
||||
xxl-job-admin:
|
||||
image: xuxueli/xxl-job-admin:2.4.0
|
||||
container_name: xxl-job-admin
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PARAMS: '--spring.datasource.url=jdbc:mysql://mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=root --spring.datasource.password=123456 --spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver'
|
||||
volumes:
|
||||
- ./xxljob/logs:/data/applogs
|
||||
ports:
|
||||
- 8080:8080
|
||||
networks:
|
||||
- youlai-boot
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
[mysqld]
|
||||
# 字符集与排序规则
|
||||
character-set-server = utf8mb4 # 服务端默认字符集
|
||||
collation-server = utf8mb4_0900_ai_ci # 服务端默认排序规则
|
||||
|
||||
# 网络与路径
|
||||
datadir = /var/lib/mysql # 数据文件存放的目录
|
||||
bind-address = 0.0.0.0 # 允许远程连接,默认 127.0.0.1 只允许本地连接
|
||||
port = 3306 # 显式指定端口(默认3306可不写)
|
||||
|
||||
# 客户端字符集同步(避免乱码)
|
||||
init_connect = 'SET NAMES utf8mb4' # 连接初始化时设置字符集
|
||||
|
||||
[client]
|
||||
default-character-set = utf8mb4 # 客户端默认字符集
|
||||
|
||||
[mysql]
|
||||
default-character-set = utf8mb4 # MySQL 命令行工具字符集
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
|
||||
# Docker Compose 安装中间件 MySQL、Redis、Minio、Xxl-Job
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
docker-compose -f ./docker-compose.yml -p youlai-boot up -d
|
||||
```
|
||||
|
||||
- p youlai-boot 指定命名空间,避免与其他容器冲突,这里方便管理,统一管理和卸载
|
||||
|
||||
## 卸载
|
||||
```bash
|
||||
docker-compose -f ./docker-compose.yml -p youlai-boot down
|
||||
```
|
||||
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>youlai-boot</artifactId>
|
||||
<version>4.3.0</version>
|
||||
<description>基于 Java 17 + SpringBoot 4 + Spring Security 构建的权限管理系统。</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>4.0.1</version> <!-- lookup parent from repository -->
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
|
||||
<hutool.version>5.8.41</hutool.version>
|
||||
|
||||
<mysql-connector-j.version>9.1.0</mysql-connector-j.version>
|
||||
<druid.version>1.2.24</druid.version>
|
||||
<!-- Spring Boot 4.x 必须使用更新的版本 -->
|
||||
<mybatis-plus.version>3.5.15</mybatis-plus.version>
|
||||
<dynamic-datasource.version>4.3.1</dynamic-datasource.version>
|
||||
|
||||
<knife4j.version>4.5.0</knife4j.version>
|
||||
|
||||
<mapstruct.version>1.6.3</mapstruct.version>
|
||||
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
|
||||
|
||||
<xxl-job.version>3.2.0</xxl-job.version>
|
||||
|
||||
<fastexcel.version>1.3.0</fastexcel.version>
|
||||
|
||||
<!-- 对象存储 -->
|
||||
<minio.version>8.5.10</minio.version>
|
||||
<okhttp3.version>4.8.1</okhttp3.version>
|
||||
|
||||
<aliyun-sdk-oss.version>3.16.3</aliyun-sdk-oss.version>
|
||||
|
||||
<!-- redisson 分布式锁 -->
|
||||
<redisson.version>4.1.0</redisson.version>
|
||||
|
||||
<!-- 自动代码生成 -->
|
||||
<mybatis-plus-generator.version>3.5.6</mybatis-plus-generator.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
|
||||
<!-- IP 地区转换 -->
|
||||
<ip2region.version>2.7.0</ip2region.version>
|
||||
|
||||
<!-- 阿里云短信 -->
|
||||
<aliyun.java.sdk.core.version>4.7.6</aliyun.java.sdk.core.version>
|
||||
<aliyun.java.sdk.dysmsapi.version>2.2.1</aliyun.java.sdk.dysmsapi.version>
|
||||
|
||||
<caffeine.version>2.9.3</caffeine.version>
|
||||
|
||||
<!-- 阿里 TransmittableThreadLocal (支持异步场景的ThreadLocal传递) -->
|
||||
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
|
||||
|
||||
<weixin-java-miniapp.version>4.8.1.B</weixin-java-miniapp.version>
|
||||
|
||||
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<!--编译测试环境,不打包在lib-->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TransmittableThreadLocal: 支持异步场景的租户上下文传递 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
<version>${transmittable-thread-local.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 允许使用Lombok的Java Bean类中使用MapStruct注解 (Lombok 1.18.20+) -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>${lombok-mapstruct-binding.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-cache</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot 4.x 已改名 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aspectj</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql-connector-j.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot 4.x 必须使用boot4版本 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-jsqlparser</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- knife4j 接口文档 -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.8.9</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MapStruct 对象映射 -->
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- xxl-job 定时任务 -->
|
||||
<dependency>
|
||||
<groupId>com.xuxueli</groupId>
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
<version>${xxl-job.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Excel 工具(EasyExcel-PLus ) -->
|
||||
<dependency>
|
||||
<groupId>cn.idev.excel</groupId>
|
||||
<artifactId>fastexcel</artifactId>
|
||||
<version>${fastexcel.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MinIO 对象存储 -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>${minio.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 阿里云 OSS 对象存储 -->
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>${aliyun-sdk-oss.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- redisson 分布式锁 -->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- mybatis-plus 代码生成器 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId>
|
||||
<version>${mybatis-plus-generator.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- velocity 模板引擎(代码生成) -->
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
<version>${velocity.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- IP 转省市区 -->
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
<version>${ip2region.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||
<version>${aliyun.java.sdk.core.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
|
||||
<version>${aliyun.java.sdk.dysmsapi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 本地缓存 -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>${caffeine.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 微信小程序登录 -->
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-miniapp</artifactId>
|
||||
<version>${weixin-java-miniapp.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 动态多数据源 -->
|
||||
<!--<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||
<version>${dynamic-datasource.version}</version>
|
||||
</dependency>-->
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
ALTER TABLE `xxk_wallet_recharge_order`
|
||||
ADD COLUMN `gift_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '充值赠送金额' AFTER `amount`,
|
||||
ADD COLUMN `credited_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '实际到账金额' AFTER `gift_amount`,
|
||||
ADD COLUMN `promotion_rule_snapshot` text NULL COMMENT '命中的充值活动规则快照' AFTER `credited_amount`;
|
||||
|
||||
UPDATE `xxk_wallet_recharge_order`
|
||||
SET `gift_amount` = IFNULL(`gift_amount`, 0.00),
|
||||
`credited_amount` = CASE
|
||||
WHEN IFNULL(`credited_amount`, 0.00) > 0 THEN `credited_amount`
|
||||
ELSE IFNULL(`amount`, 0.00) + IFNULL(`gift_amount`, 0.00)
|
||||
END;
|
||||
@@ -0,0 +1,19 @@
|
||||
DROP TABLE IF EXISTS `xxk_proxy_region`;
|
||||
CREATE TABLE `xxk_proxy_region` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`region_code` varchar(64) NOT NULL COMMENT '地区编号',
|
||||
`region_name_zh` varchar(100) NOT NULL COMMENT '地区中文名',
|
||||
`region_name` varchar(100) NOT NULL COMMENT '地区名称',
|
||||
`icon_url` varchar(500) NOT NULL COMMENT '地区图标',
|
||||
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(1-启用 0-禁用)',
|
||||
`sort` int NOT NULL DEFAULT '0' COMMENT '排序',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` bigint DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` bigint DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除(1-已删 0-未删)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_proxy_region_code_deleted` (`region_code`,`is_deleted`),
|
||||
KEY `idx_proxy_region_status` (`status`,`is_deleted`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代理平台地区管理表';
|
||||
@@ -0,0 +1,47 @@
|
||||
-- 代理模式改造升级脚本
|
||||
-- 适用范围:
|
||||
-- 1. 将会员资料补充代理级别字段。
|
||||
-- 2. 为历史会员回填默认代理级别为二级代理。
|
||||
-- 3. 规范异常代理级别值,保证新代码可直接使用。
|
||||
--
|
||||
-- 执行建议:
|
||||
-- 1. 执行前先备份数据库。
|
||||
-- 2. 在业务低峰期执行。
|
||||
-- 3. 执行完成后再发布本次 Java / UI / member-web 代码。
|
||||
|
||||
SET @current_schema = DATABASE();
|
||||
|
||||
-- 1) 如果 xxk_member_profile.agent_level 不存在,则新增字段
|
||||
SET @has_agent_level = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'xxk_member_profile'
|
||||
AND COLUMN_NAME = 'agent_level'
|
||||
);
|
||||
|
||||
SET @ddl_sql = IF(
|
||||
@has_agent_level = 0,
|
||||
'ALTER TABLE `xxk_member_profile`
|
||||
ADD COLUMN `agent_level` TINYINT NOT NULL DEFAULT 2 COMMENT ''代理级别(1一级代理 2二级代理)'' AFTER `invite_code`',
|
||||
'SELECT ''xxk_member_profile.agent_level already exists'' AS message'
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @ddl_sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 2) 历史数据回填:
|
||||
-- 空值、0、其他非法值一律收敛成二级代理
|
||||
UPDATE `xxk_member_profile`
|
||||
SET `agent_level` = 2
|
||||
WHERE `agent_level` IS NULL
|
||||
OR `agent_level` NOT IN (1, 2);
|
||||
|
||||
|
||||
UPDATE `sys_menu` SET `name` = '代理返佣流水' WHERE `id` = 1200
|
||||
|
||||
|
||||
ALTER TABLE `xxk_distribution_config`
|
||||
ADD COLUMN IF NOT EXISTS `withdraw_threshold` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '提现门槛金额' AFTER `second_level_rate`,
|
||||
ADD COLUMN IF NOT EXISTS `withdraw_fee_rate` decimal(10,4) NOT NULL DEFAULT 0.0000 COMMENT '提现手续费比例' AFTER `withdraw_threshold`;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- 修复会员分销字段漏合并导致的后台用户中心查询报错。
|
||||
-- 适用于已存在 xxk_distribution_relation / xxk_distribution_commission 老结构的数据库。
|
||||
|
||||
ALTER TABLE `xxk_distribution_relation`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '当前会员ID' AFTER `grand_parent_user_id`,
|
||||
ADD COLUMN `parent_member_user_id` BIGINT NULL COMMENT '一级上级会员ID' AFTER `member_user_id`,
|
||||
ADD COLUMN `grand_parent_member_user_id` BIGINT NULL COMMENT '二级上级会员ID' AFTER `parent_member_user_id`,
|
||||
ADD UNIQUE INDEX `uk_xxk_distribution_relation_member_user_id` (`member_user_id`),
|
||||
ADD KEY `idx_xxk_distribution_relation_parent_member_user_id` (`parent_member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_distribution_commission`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '佣金归属会员ID' AFTER `from_user_id`,
|
||||
ADD COLUMN `from_member_user_id` BIGINT NULL COMMENT '消费会员ID' AFTER `member_user_id`,
|
||||
ADD KEY `idx_xxk_distribution_commission_member_user_id` (`member_user_id`),
|
||||
ADD KEY `idx_xxk_distribution_commission_from_member_user_id` (`from_member_user_id`);
|
||||
@@ -0,0 +1,8 @@
|
||||
-- 分销关系表 user_id 可空兼容升级脚本
|
||||
-- 说明:
|
||||
-- 1. 历史设计中 xxk_distribution_relation 仅面向 sys_user,user_id 为 NOT NULL。
|
||||
-- 2. 会员体系接入后,会员邀请关系只依赖 member_user_id / parent_member_user_id。
|
||||
-- 3. 若继续要求 user_id 非空,会员注册或邀请码绑定时会因未传 sys_user.user_id 而写库失败。
|
||||
|
||||
ALTER TABLE `xxk_distribution_relation`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '当前用户ID';
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `xxk_distribution_config`
|
||||
ADD COLUMN `withdraw_threshold` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '提现门槛金额' AFTER `second_level_rate`,
|
||||
ADD COLUMN `withdraw_fee_rate` decimal(10,4) NOT NULL DEFAULT 0.0000 COMMENT '提现手续费比例' AFTER `withdraw_threshold`;
|
||||
@@ -0,0 +1,15 @@
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`, `always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(12010, 1200, '0,1100,1200', '提现申请', 'C', 'ProxyDistributionWithdraw', 'distribution-withdraw', 'proxy/distribution-withdraw/index', NULL, 0, 1, 1, 20, 'Wallet', NULL, NOW(), NOW(), NULL),
|
||||
(12011, 12010, '0,1100,1200,12010', '提现申请查询', 'B', NULL, '', NULL, 'proxy:distribution:withdraw:list', NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL),
|
||||
(12012, 12010, '0,1100,1200,12010', '提现申请审核', 'B', NULL, '', NULL, 'proxy:distribution:withdraw:audit', NULL, NULL, 1, 2, '', NULL, NOW(), NOW(), NULL),
|
||||
(12013, 12010, '0,1100,1200,12010', '提现确认打款', 'B', NULL, '', NULL, 'proxy:distribution:withdraw:pay', NULL, NULL, 1, 3, '', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`update_time` = NOW();
|
||||
@@ -0,0 +1,69 @@
|
||||
-- 分销佣金提现模块升级脚本
|
||||
-- 说明:
|
||||
-- 1. 在分销佣金账户中增加累计提现金额。
|
||||
-- 2. 在分销佣金账户流水中增加冻结余额与提现单关联字段。
|
||||
-- 3. 新增会员提现收款方式表与提现申请表。
|
||||
|
||||
ALTER TABLE `xxk_distribution_account`
|
||||
ADD COLUMN `total_withdraw_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计提现金额' AFTER `total_transfer_amount`;
|
||||
|
||||
ALTER TABLE `xxk_distribution_account_flow`
|
||||
ADD COLUMN `before_frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动前冻结余额' AFTER `after_balance`,
|
||||
ADD COLUMN `after_frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动后冻结余额' AFTER `before_frozen_balance`,
|
||||
ADD COLUMN `related_withdraw_no` VARCHAR(64) DEFAULT NULL COMMENT '关联提现单号' AFTER `related_wallet_flow_no`,
|
||||
ADD KEY `idx_xxk_distribution_account_flow_related_withdraw_no` (`related_withdraw_no`);
|
||||
|
||||
CREATE TABLE `xxk_distribution_withdraw_method` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`method_type` VARCHAR(32) NOT NULL COMMENT '收款方式(ALIPAY_ACCOUNT/ALIPAY_QR/WECHAT_QR)',
|
||||
`account_name` VARCHAR(64) NOT NULL COMMENT '收款人姓名',
|
||||
`account_no` VARCHAR(128) DEFAULT NULL COMMENT '收款账号',
|
||||
`qr_code_url` VARCHAR(255) DEFAULT NULL COMMENT '收款码地址',
|
||||
`is_default` TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认(1-是 0-否)',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_xxk_distribution_withdraw_method_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_distribution_withdraw_method_default` (`member_user_id`, `is_default`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销提现收款方式表';
|
||||
|
||||
CREATE TABLE `xxk_distribution_withdraw_apply` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`withdraw_no` VARCHAR(64) NOT NULL COMMENT '提现单号',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`username_snapshot` VARCHAR(64) DEFAULT NULL COMMENT '会员账号快照',
|
||||
`nickname_snapshot` VARCHAR(64) DEFAULT NULL COMMENT '会员昵称快照',
|
||||
`mobile_snapshot` VARCHAR(32) DEFAULT NULL COMMENT '会员手机号快照',
|
||||
`withdraw_method_id` BIGINT DEFAULT NULL COMMENT '提现方式ID',
|
||||
`method_type` VARCHAR(32) NOT NULL COMMENT '收款方式类型',
|
||||
`account_name_snapshot` VARCHAR(64) NOT NULL COMMENT '收款人快照',
|
||||
`account_no_snapshot` VARCHAR(128) DEFAULT NULL COMMENT '收款账号快照',
|
||||
`qr_code_url_snapshot` VARCHAR(255) DEFAULT NULL COMMENT '收款码快照',
|
||||
`amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '申请提现金额',
|
||||
`paid_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '实际打款金额',
|
||||
`status` VARCHAR(32) NOT NULL COMMENT '状态(PENDING/APPROVED/REJECTED/PAID)',
|
||||
`submit_remark` VARCHAR(255) DEFAULT NULL COMMENT '申请备注',
|
||||
`submit_time` DATETIME DEFAULT NULL COMMENT '申请时间',
|
||||
`audit_remark` VARCHAR(255) DEFAULT NULL COMMENT '审核备注',
|
||||
`audit_by` BIGINT DEFAULT NULL COMMENT '审核人',
|
||||
`audit_time` DATETIME DEFAULT NULL COMMENT '审核时间',
|
||||
`pay_remark` VARCHAR(255) DEFAULT NULL COMMENT '打款备注',
|
||||
`pay_proof_urls_json` TEXT DEFAULT NULL COMMENT '打款凭证JSON',
|
||||
`pay_by` BIGINT DEFAULT NULL COMMENT '打款确认人',
|
||||
`pay_time` DATETIME DEFAULT NULL COMMENT '打款时间',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_distribution_withdraw_apply_no` (`withdraw_no`),
|
||||
KEY `idx_xxk_distribution_withdraw_apply_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_distribution_withdraw_apply_status` (`status`),
|
||||
KEY `idx_xxk_distribution_withdraw_apply_submit_time` (`submit_time`),
|
||||
KEY `idx_xxk_distribution_withdraw_apply_pay_time` (`pay_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销提现申请表';
|
||||
@@ -0,0 +1,10 @@
|
||||
-- 会员代理级别升级脚本
|
||||
-- 1. 在会员资料表增加代理级别字段,默认二级代理。
|
||||
-- 2. 回填历史空值,保证会员前台可显示代理级别和返佣比例。
|
||||
|
||||
ALTER TABLE `xxk_member_profile`
|
||||
ADD COLUMN `agent_level` TINYINT NOT NULL DEFAULT 2 COMMENT '代理级别(1一级代理 2二级代理)' AFTER `invite_code`;
|
||||
|
||||
UPDATE `xxk_member_profile`
|
||||
SET `agent_level` = 2
|
||||
WHERE `agent_level` IS NULL;
|
||||
@@ -0,0 +1,62 @@
|
||||
-- 会员分销邀请码与独立佣金账户升级脚本
|
||||
-- 说明:
|
||||
-- 1. 为会员资料增加邀请码字段,支持邀请绑定。
|
||||
-- 2. 为分销关系与佣金流水增加 member_user_id 口径,避免与后台 sys_user 混用。
|
||||
-- 3. 新增独立佣金账户与账户流水,佣金可转入钱包,但不直接混入钱包口径。
|
||||
|
||||
ALTER TABLE `xxk_member_profile`
|
||||
ADD COLUMN `invite_code` VARCHAR(32) NULL COMMENT '邀请码' AFTER `member_user_id`,
|
||||
ADD UNIQUE INDEX `uk_xxk_member_profile_invite_code` (`invite_code`);
|
||||
|
||||
ALTER TABLE `xxk_distribution_relation`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '当前会员ID' AFTER `grand_parent_user_id`,
|
||||
ADD COLUMN `parent_member_user_id` BIGINT NULL COMMENT '一级上级会员ID' AFTER `member_user_id`,
|
||||
ADD COLUMN `grand_parent_member_user_id` BIGINT NULL COMMENT '二级上级会员ID' AFTER `parent_member_user_id`,
|
||||
ADD UNIQUE INDEX `uk_xxk_distribution_relation_member_user_id` (`member_user_id`),
|
||||
ADD KEY `idx_xxk_distribution_relation_parent_member_user_id` (`parent_member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_distribution_commission`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '佣金归属会员ID' AFTER `from_user_id`,
|
||||
ADD COLUMN `from_member_user_id` BIGINT NULL COMMENT '消费会员ID' AFTER `member_user_id`,
|
||||
ADD KEY `idx_xxk_distribution_commission_member_user_id` (`member_user_id`),
|
||||
ADD KEY `idx_xxk_distribution_commission_from_member_user_id` (`from_member_user_id`);
|
||||
|
||||
CREATE TABLE `xxk_distribution_account` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`user_id` BIGINT DEFAULT NULL COMMENT '后台用户ID',
|
||||
`member_user_id` BIGINT DEFAULT NULL COMMENT '会员ID',
|
||||
`available_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '可用佣金余额',
|
||||
`frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '冻结佣金余额',
|
||||
`total_earned_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计获得佣金',
|
||||
`total_revoked_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计回退佣金',
|
||||
`total_transfer_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计转入钱包金额',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(1正常 0禁用)',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_distribution_account_member_user_id` (`member_user_id`),
|
||||
UNIQUE KEY `uk_xxk_distribution_account_user_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销佣金账户表';
|
||||
|
||||
CREATE TABLE `xxk_distribution_account_flow` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`flow_no` VARCHAR(64) NOT NULL COMMENT '流水号',
|
||||
`user_id` BIGINT DEFAULT NULL COMMENT '后台用户ID',
|
||||
`member_user_id` BIGINT DEFAULT NULL COMMENT '会员ID',
|
||||
`commission_id` BIGINT DEFAULT NULL COMMENT '关联佣金记录ID',
|
||||
`biz_type` VARCHAR(32) NOT NULL COMMENT '业务类型',
|
||||
`change_type` VARCHAR(16) NOT NULL COMMENT '变动类型(IN/OUT)',
|
||||
`change_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动金额',
|
||||
`before_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动前余额',
|
||||
`after_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动后余额',
|
||||
`related_order_no` VARCHAR(64) DEFAULT NULL COMMENT '关联订单号',
|
||||
`related_wallet_flow_no` VARCHAR(64) DEFAULT NULL COMMENT '关联钱包流水号',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`operate_by` BIGINT DEFAULT NULL COMMENT '操作人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_distribution_account_flow_no` (`flow_no`),
|
||||
KEY `idx_xxk_distribution_account_flow_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_distribution_account_flow_commission_id` (`commission_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销佣金账户流水表';
|
||||
@@ -0,0 +1,88 @@
|
||||
-- 会员体系初始化脚本
|
||||
-- 执行说明:
|
||||
-- 1. 本脚本只新增会员体系核心表,不影响现有后台管理员与代理业务表。
|
||||
-- 2. 后续当订单、白名单、钱包正式切换到会员主账号时,再补业务迁移脚本。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_member_user` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`username` VARCHAR(64) NOT NULL COMMENT '用户名',
|
||||
`nickname` VARCHAR(64) DEFAULT NULL COMMENT '昵称',
|
||||
`password` VARCHAR(255) DEFAULT NULL COMMENT '密码',
|
||||
`avatar` VARCHAR(255) DEFAULT NULL COMMENT '头像',
|
||||
`mobile` VARCHAR(20) NOT NULL COMMENT '手机号',
|
||||
`email` VARCHAR(128) DEFAULT NULL COMMENT '邮箱',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(1正常 0禁用)',
|
||||
`source` VARCHAR(32) NOT NULL DEFAULT 'MANUAL' COMMENT '注册来源',
|
||||
`wx_openid` VARCHAR(64) DEFAULT NULL COMMENT '微信openid',
|
||||
`wx_unionid` VARCHAR(64) DEFAULT NULL COMMENT '微信unionid',
|
||||
`last_login_time` DATETIME DEFAULT NULL COMMENT '最后登录时间',
|
||||
`last_login_ip` VARCHAR(64) DEFAULT NULL COMMENT '最后登录IP',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_member_user_mobile` (`mobile`),
|
||||
UNIQUE KEY `uk_xxk_member_user_username` (`username`),
|
||||
KEY `idx_xxk_member_user_status` (`status`),
|
||||
KEY `idx_xxk_member_user_source` (`source`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员主账号表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_member_profile` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`agent_level` TINYINT NOT NULL DEFAULT 2 COMMENT '代理级别(1一级代理 2二级代理)',
|
||||
`company_name` VARCHAR(128) DEFAULT NULL COMMENT '公司名称',
|
||||
`contact_name` VARCHAR(64) DEFAULT NULL COMMENT '联系人',
|
||||
`contact_wechat` VARCHAR(64) DEFAULT NULL COMMENT '联系微信',
|
||||
`email` VARCHAR(128) DEFAULT NULL COMMENT '联系邮箱',
|
||||
`qq` VARCHAR(32) DEFAULT NULL COMMENT 'QQ',
|
||||
`address` VARCHAR(255) DEFAULT NULL COMMENT '地址',
|
||||
`industry` VARCHAR(64) DEFAULT NULL COMMENT '行业',
|
||||
`realname_status` TINYINT NOT NULL DEFAULT 0 COMMENT '实名状态(0未认证 1已认证)',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_member_profile_user` (`member_user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员资料表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_member_wallet` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '余额',
|
||||
`frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '冻结金额',
|
||||
`total_recharge_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计充值金额',
|
||||
`total_consume_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计消费金额',
|
||||
`total_refund_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计退款金额',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(1正常 0禁用)',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_member_wallet_user` (`member_user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员钱包表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_member_login_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`member_user_id` BIGINT DEFAULT NULL COMMENT '会员ID',
|
||||
`mobile` VARCHAR(20) DEFAULT NULL COMMENT '登录手机号',
|
||||
`login_type` VARCHAR(32) NOT NULL COMMENT '登录类型',
|
||||
`login_ip` VARCHAR(64) DEFAULT NULL COMMENT '登录IP',
|
||||
`login_region` VARCHAR(128) DEFAULT NULL COMMENT '登录地区',
|
||||
`device_info` VARCHAR(255) DEFAULT NULL COMMENT '设备信息',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '登录结果(1成功 0失败)',
|
||||
`message` VARCHAR(255) DEFAULT NULL COMMENT '结果说明',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_xxk_member_login_log_user` (`member_user_id`),
|
||||
KEY `idx_xxk_member_login_log_mobile` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员登录日志表';
|
||||
@@ -0,0 +1,54 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 会员管理菜单初始化
|
||||
-- 说明:
|
||||
-- 1. 本脚本负责会员后台管理菜单、按钮权限和系统管理员授权。
|
||||
-- 2. 会员前台站点后续独立建设,此处仅覆盖管理后台会员运营入口。
|
||||
|
||||
-- 一级目录:会员中心
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1300, 0, '0', '会员中心', 'C', 'Member', '/member', 'Layout', NULL,
|
||||
1, NULL, 1, 11, 'user-filled', '/member/user', now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`always_show` = VALUES(`always_show`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`redirect` = VALUES(`redirect`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 会员管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1310, 1300, '0,1300', '会员管理', 'M', 'MemberUser', 'user', 'member/user/index', NULL,
|
||||
0, 1, 1, 1, 'avatar', NULL, now(), now(), NULL),
|
||||
(13101, 1310, '0,1300,1310', '会员查询', 'B', NULL, '', NULL, 'member:user:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(13102, 1310, '0,1300,1310', '会员新增', 'B', NULL, '', NULL, 'member:user:create',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(13103, 1310, '0,1300,1310', '会员修改', 'B', NULL, '', NULL, 'member:user:update',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 系统管理员授权
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 1300),
|
||||
(2, 1310), (2, 13101), (2, 13102), (2, 13103);
|
||||
@@ -0,0 +1,41 @@
|
||||
-- 会员代理业务 user_id 兼容升级脚本
|
||||
-- 说明:
|
||||
-- 1. 当前代理业务表最初按 sys_user 体系设计,user_id 为 NOT NULL。
|
||||
-- 2. 会员体系接入后,会员侧业务主要依赖 member_user_id,很多写入场景不会再写 user_id。
|
||||
-- 3. 因此需要将相关表的 user_id 调整为可空,避免会员新增、会员下单、钱包、静态/动态资源落库时报错。
|
||||
|
||||
ALTER TABLE `xxk_wallet_account`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
ALTER TABLE `xxk_wallet_flow`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
ALTER TABLE `xxk_proxy_order`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
ALTER TABLE `xxk_proxy_order_item`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
ALTER TABLE `xxk_static_proxy_asset`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
ALTER TABLE `xxk_static_proxy_whitelist`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
ALTER TABLE `xxk_dynamic_channel`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
ALTER TABLE `xxk_dynamic_channel_traffic_log`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
ALTER TABLE `xxk_dynamic_proxy_generate_log`
|
||||
MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
-- 会员钱包账户应保持 member_user_id 唯一,避免同一会员生成多条钱包账户
|
||||
ALTER TABLE `xxk_wallet_account`
|
||||
DROP INDEX `idx_xxk_wallet_account_member_user_id`,
|
||||
ADD UNIQUE INDEX `uk_xxk_wallet_account_member_user_id` (`member_user_id`);
|
||||
|
||||
-- 会员白名单也应按 member_user_id + whitelist_ip 去重
|
||||
ALTER TABLE `xxk_static_proxy_whitelist`
|
||||
ADD UNIQUE INDEX `uk_member_whitelist_ip` (`member_user_id`, `whitelist_ip`);
|
||||
@@ -0,0 +1,49 @@
|
||||
-- 会员代理业务归属升级脚本
|
||||
-- 说明:
|
||||
-- 1. 为核心代理业务表增加 member_user_id 字段,逐步从 sys_user 归属切到 member_user。
|
||||
-- 2. 该脚本只做结构升级,不强制迁移历史数据。
|
||||
|
||||
ALTER TABLE `xxk_proxy_order`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_proxy_order_member_user_id` (`member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_proxy_order_item`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_proxy_order_item_member_user_id` (`member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_wallet_account`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_wallet_account_member_user_id` (`member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_wallet_flow`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_wallet_flow_member_user_id` (`member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_static_proxy_asset`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_static_proxy_asset_member_user_id` (`member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_static_proxy_whitelist`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_static_proxy_whitelist_member_user_id` (`member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_dynamic_channel`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_dynamic_channel_member_user_id` (`member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_dynamic_channel_traffic_log`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_dynamic_channel_traffic_log_member_user_id` (`member_user_id`);
|
||||
|
||||
ALTER TABLE `xxk_dynamic_proxy_generate_log`
|
||||
ADD COLUMN `member_user_id` BIGINT NULL COMMENT '会员ID' AFTER `user_id`,
|
||||
ADD KEY `idx_xxk_dynamic_proxy_generate_log_member_user_id` (`member_user_id`);
|
||||
|
||||
-- 历史数据迁移建议:
|
||||
-- 可按手机号将 sys_user 关联到 xxk_member_user 后,回填 member_user_id。
|
||||
-- 示例思路:
|
||||
-- UPDATE xxk_wallet_account wa
|
||||
-- JOIN sys_user su ON su.id = wa.user_id
|
||||
-- JOIN xxk_member_user mu ON mu.mobile = su.mobile
|
||||
-- SET wa.member_user_id = mu.id
|
||||
-- WHERE wa.member_user_id IS NULL;
|
||||
@@ -0,0 +1,61 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 会员认证菜单初始化
|
||||
-- 说明:
|
||||
-- 1. 在系统管理下新增“业务配置”统一入口,当前承载会员认证策略与会员登录注册配置。
|
||||
-- 2. 在会员中心下新增“认证审核”页面,用于后台审核会员认证申请。
|
||||
|
||||
-- 系统管理 > 业务配置
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(271, 1, '0,1', '业务配置', 'M', 'BizConfigCenter', 'biz-config', 'system/biz-config/index', NULL,
|
||||
0, 1, 1, 8, 'setting', NULL, NOW(), NOW(), NULL),
|
||||
(2711, 271, '0,1,271', '会员认证策略查看', 'B', NULL, '', NULL, 'member:verify:policy:view',
|
||||
NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL),
|
||||
(2712, 271, '0,1,271', '会员认证策略修改', 'B', NULL, '', NULL, 'member:verify:policy:update',
|
||||
NULL, NULL, 1, 2, '', NULL, NOW(), NOW(), NULL),
|
||||
(2713, 271, '0,1,271', '会员登录注册配置查看', 'B', NULL, '', NULL, 'member:auth:config:view',
|
||||
NULL, NULL, 1, 3, '', NULL, NOW(), NOW(), NULL),
|
||||
(2714, 271, '0,1,271', '会员登录注册配置修改', 'B', NULL, '', NULL, 'member:auth:config:update',
|
||||
NULL, NULL, 1, 4, '', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`update_time` = NOW();
|
||||
|
||||
-- 会员中心 > 认证审核
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1320, 1300, '0,1300', '认证审核', 'M', 'MemberVerifyAudit', 'verify', 'member/verify/index', NULL,
|
||||
0, 1, 1, 2, 'checked', NULL, NOW(), NOW(), NULL),
|
||||
(13201, 1320, '0,1300,1320', '认证记录查询', 'B', NULL, '', NULL, 'member:verify:record:list',
|
||||
NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL),
|
||||
(13202, 1320, '0,1300,1320', '认证审核处理', 'B', NULL, '', NULL, 'member:verify:record:audit',
|
||||
NULL, NULL, 1, 2, '', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`update_time` = NOW();
|
||||
|
||||
-- 系统管理员授权
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 271), (2, 2711), (2, 2712), (2, 2713), (2, 2714),
|
||||
(2, 1320), (2, 13201), (2, 13202);
|
||||
@@ -0,0 +1,68 @@
|
||||
-- 会员认证配置与审核升级脚本
|
||||
-- 说明:
|
||||
-- 1. 扩展 sys_config 配置值长度,支持结构化 JSON 策略。
|
||||
-- 2. 为会员资料表补充认证状态与最近一次审核信息。
|
||||
-- 3. 新增会员认证记录表,保存提交快照和审核结果。
|
||||
|
||||
ALTER TABLE `sys_config`
|
||||
MODIFY COLUMN `config_value` TEXT NOT NULL COMMENT '配置值';
|
||||
|
||||
ALTER TABLE `xxk_member_profile`
|
||||
ADD COLUMN `real_name` VARCHAR(64) NULL COMMENT '真实姓名' AFTER `member_user_id`,
|
||||
ADD COLUMN `id_card_no` VARCHAR(32) NULL COMMENT '身份证号' AFTER `real_name`,
|
||||
ADD COLUMN `id_card_front_url` VARCHAR(255) NULL COMMENT '身份证人像面' AFTER `id_card_no`,
|
||||
ADD COLUMN `id_card_back_url` VARCHAR(255) NULL COMMENT '身份证国徽面' AFTER `id_card_front_url`,
|
||||
ADD COLUMN `support_docs_json` TEXT NULL COMMENT '补充材料JSON' AFTER `id_card_back_url`,
|
||||
MODIFY COLUMN `realname_status` TINYINT NOT NULL DEFAULT 0 COMMENT '实名状态(0未提交 1待审核 2已认证 3已驳回)',
|
||||
ADD COLUMN `last_verify_record_id` BIGINT NULL COMMENT '最近认证记录ID' AFTER `realname_status`,
|
||||
ADD COLUMN `last_submit_time` DATETIME NULL COMMENT '最近提交时间' AFTER `last_verify_record_id`,
|
||||
ADD COLUMN `verified_time` DATETIME NULL COMMENT '认证通过时间' AFTER `last_submit_time`,
|
||||
ADD COLUMN `audit_time` DATETIME NULL COMMENT '审核时间' AFTER `verified_time`,
|
||||
ADD COLUMN `audit_by` BIGINT NULL COMMENT '审核人ID' AFTER `audit_time`,
|
||||
ADD COLUMN `audit_remark` VARCHAR(255) NULL COMMENT '审核备注' AFTER `audit_by`;
|
||||
|
||||
CREATE TABLE `xxk_member_verify_record` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`verify_no` VARCHAR(64) NOT NULL COMMENT '认证单号',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`username_snapshot` VARCHAR(64) DEFAULT NULL COMMENT '用户名快照',
|
||||
`nickname_snapshot` VARCHAR(64) DEFAULT NULL COMMENT '昵称快照',
|
||||
`mobile_snapshot` VARCHAR(20) DEFAULT NULL COMMENT '手机号快照',
|
||||
`real_name` VARCHAR(64) DEFAULT NULL COMMENT '真实姓名',
|
||||
`id_card_no` VARCHAR(32) DEFAULT NULL COMMENT '身份证号',
|
||||
`id_card_front_url` VARCHAR(255) DEFAULT NULL COMMENT '身份证人像面',
|
||||
`id_card_back_url` VARCHAR(255) DEFAULT NULL COMMENT '身份证国徽面',
|
||||
`support_docs_json` TEXT DEFAULT NULL COMMENT '补充材料JSON',
|
||||
`policy_snapshot_json` LONGTEXT DEFAULT NULL COMMENT '策略快照JSON',
|
||||
`form_data_json` LONGTEXT DEFAULT NULL COMMENT '提交数据JSON',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(1待审核 2通过 3驳回)',
|
||||
`submit_remark` VARCHAR(255) DEFAULT NULL COMMENT '提交备注',
|
||||
`submit_time` DATETIME DEFAULT NULL COMMENT '提交时间',
|
||||
`audit_time` DATETIME DEFAULT NULL COMMENT '审核时间',
|
||||
`audit_by` BIGINT DEFAULT NULL COMMENT '审核人ID',
|
||||
`audit_remark` VARCHAR(255) DEFAULT NULL COMMENT '审核备注',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_member_verify_record_no` (`verify_no`),
|
||||
KEY `idx_xxk_member_verify_record_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_member_verify_record_status` (`status`),
|
||||
KEY `idx_xxk_member_verify_record_submit_time` (`submit_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员认证记录表';
|
||||
|
||||
INSERT INTO `sys_config` (`config_name`, `config_key`, `config_value`, `remark`, `create_time`, `create_by`, `is_deleted`)
|
||||
SELECT
|
||||
'会员认证策略',
|
||||
'member.verify.policy',
|
||||
'{"enabled":true,"forceRequired":false,"forceScenes":[],"fields":[{"code":"mobile","label":"手机号","type":"TEXT","enabled":true,"required":true,"readonly":true,"source":"ACCOUNT","maxCount":1,"sort":10,"placeholder":"自动读取账号手机号"},{"code":"realName","label":"姓名","type":"TEXT","enabled":true,"required":true,"readonly":false,"source":"USER_INPUT","maxCount":1,"sort":20,"placeholder":"请输入真实姓名"},{"code":"idCardNo","label":"身份证号","type":"TEXT","enabled":true,"required":true,"readonly":false,"source":"USER_INPUT","maxCount":1,"sort":30,"placeholder":"请输入身份证号"},{"code":"idCardFrontUrl","label":"身份证人像面","type":"IMAGE","enabled":true,"required":true,"readonly":false,"source":"USER_INPUT","maxCount":1,"sort":40,"placeholder":"请上传身份证人像面"},{"code":"idCardBackUrl","label":"身份证国徽面","type":"IMAGE","enabled":true,"required":true,"readonly":false,"source":"USER_INPUT","maxCount":1,"sort":50,"placeholder":"请上传身份证国徽面"},{"code":"supportDocs","label":"其他图片辅证","type":"IMAGE_LIST","enabled":true,"required":false,"readonly":false,"source":"USER_INPUT","maxCount":5,"sort":60,"placeholder":"可上传补充图片材料"}]}',
|
||||
'会员实名认证表单与强制校验策略',
|
||||
NOW(),
|
||||
1,
|
||||
0
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `sys_config` WHERE `config_key` = 'member.verify.policy'
|
||||
);
|
||||
@@ -0,0 +1,69 @@
|
||||
-- API 独立账户初始化脚本
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_account` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`open_api_app_id` BIGINT NOT NULL COMMENT '开放应用ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`balance` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '余额',
|
||||
`frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '冻结余额',
|
||||
`total_recharge_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '累计充值金额',
|
||||
`total_consume_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '累计消费金额',
|
||||
`total_refund_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '累计退款金额',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(0停用 1正常)',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_account_app_id` (`open_api_app_id`),
|
||||
KEY `idx_xxk_open_api_account_member_user_id` (`member_user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API独立账户表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_account_flow` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`flow_no` VARCHAR(64) NOT NULL COMMENT '流水号',
|
||||
`open_api_app_id` BIGINT NOT NULL COMMENT '开放应用ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`biz_type` VARCHAR(64) NOT NULL COMMENT '业务类型',
|
||||
`change_type` VARCHAR(16) NOT NULL COMMENT '变动类型(IN/OUT)',
|
||||
`change_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '变动金额',
|
||||
`before_balance` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '变动前余额',
|
||||
`after_balance` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '变动后余额',
|
||||
`related_order_no` VARCHAR(64) DEFAULT NULL COMMENT '关联单号',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`operate_by` BIGINT DEFAULT NULL COMMENT '操作人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_account_flow_no` (`flow_no`),
|
||||
KEY `idx_xxk_open_api_account_flow_app_id` (`open_api_app_id`),
|
||||
KEY `idx_xxk_open_api_account_flow_related_order_no` (`related_order_no`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API独立账户流水表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_recharge_order` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`recharge_no` VARCHAR(64) NOT NULL COMMENT '充值单号',
|
||||
`pay_order_no` VARCHAR(64) NOT NULL COMMENT '支付单号',
|
||||
`open_api_app_id` BIGINT NOT NULL COMMENT '开放应用ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`payment_type` VARCHAR(32) NOT NULL COMMENT '支付方式',
|
||||
`pay_status` VARCHAR(32) NOT NULL COMMENT '支付状态',
|
||||
`channel_order_no` VARCHAR(64) DEFAULT NULL COMMENT '渠道订单号',
|
||||
`channel_response` LONGTEXT DEFAULT NULL COMMENT '渠道响应',
|
||||
`client_type` VARCHAR(32) DEFAULT NULL COMMENT '客户端类型',
|
||||
`return_url` VARCHAR(500) DEFAULT NULL COMMENT '回跳地址',
|
||||
`amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '充值金额',
|
||||
`gift_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '赠送金额',
|
||||
`credited_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '到账金额',
|
||||
`promotion_rule_snapshot` TEXT DEFAULT NULL COMMENT '活动规则快照',
|
||||
`currency` VARCHAR(16) DEFAULT 'USD' COMMENT '币种',
|
||||
`paid_time` DATETIME DEFAULT NULL COMMENT '支付完成时间',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_recharge_no` (`recharge_no`),
|
||||
UNIQUE KEY `uk_xxk_open_api_pay_order_no` (`pay_order_no`),
|
||||
KEY `idx_xxk_open_api_recharge_order_app_id` (`open_api_app_id`),
|
||||
KEY `idx_xxk_open_api_recharge_order_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_open_api_recharge_order_pay_status` (`pay_status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API独立账户充值单表';
|
||||
@@ -0,0 +1,25 @@
|
||||
-- 开放接口应用初始化脚本
|
||||
-- 当前版本采用 appId + appSecret 换 accessToken 的轻量模式。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_app` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`app_id` VARCHAR(64) NOT NULL COMMENT '应用ID',
|
||||
`app_name` VARCHAR(128) NOT NULL COMMENT '应用名称',
|
||||
`app_secret` VARCHAR(128) NOT NULL COMMENT '应用密钥',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '绑定会员ID',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(0停用 1启用)',
|
||||
`allow_ip_list` TEXT DEFAULT NULL COMMENT '允许访问IP列表,逗号/换行分隔',
|
||||
`last_auth_time` DATETIME DEFAULT NULL COMMENT '最近换取token时间',
|
||||
`last_auth_ip` VARCHAR(64) DEFAULT NULL COMMENT '最近换取token IP',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_app_app_id` (`app_id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_app_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_open_api_app_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_open_api_app_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放接口应用表';
|
||||
@@ -0,0 +1,33 @@
|
||||
-- 开放接口申请与审核初始化脚本
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_apply` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`apply_no` VARCHAR(64) NOT NULL COMMENT '申请单号',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`username_snapshot` VARCHAR(64) DEFAULT NULL COMMENT '用户名快照',
|
||||
`mobile_snapshot` VARCHAR(32) DEFAULT NULL COMMENT '手机号快照',
|
||||
`contact_name` VARCHAR(64) NOT NULL COMMENT '联系人',
|
||||
`contact_mobile` VARCHAR(32) DEFAULT NULL COMMENT '联系电话',
|
||||
`contact_email` VARCHAR(128) DEFAULT NULL COMMENT '联系邮箱',
|
||||
`company_name` VARCHAR(128) DEFAULT NULL COMMENT '公司名称',
|
||||
`purpose` VARCHAR(255) NOT NULL COMMENT '申请用途',
|
||||
`scenario_description` TEXT NOT NULL COMMENT '使用场景说明',
|
||||
`allow_ip_list` TEXT DEFAULT NULL COMMENT 'IP白名单',
|
||||
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态(0待审核 1已通过 2已驳回)',
|
||||
`submit_remark` VARCHAR(255) DEFAULT NULL COMMENT '补充说明',
|
||||
`submit_time` DATETIME DEFAULT NULL COMMENT '提交时间',
|
||||
`audit_time` DATETIME DEFAULT NULL COMMENT '审核时间',
|
||||
`audit_by` BIGINT DEFAULT NULL COMMENT '审核人ID',
|
||||
`audit_remark` VARCHAR(255) DEFAULT NULL COMMENT '审核备注',
|
||||
`open_api_app_id` BIGINT DEFAULT NULL COMMENT '关联开放应用ID',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_apply_no` (`apply_no`),
|
||||
KEY `idx_xxk_open_api_apply_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_open_api_apply_status` (`status`),
|
||||
KEY `idx_xxk_open_api_apply_submit_time` (`submit_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放接口申请表';
|
||||
@@ -0,0 +1,59 @@
|
||||
-- 开放API回调能力升级
|
||||
|
||||
SET @schema_name = DATABASE();
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_open_api_app'
|
||||
AND COLUMN_NAME = 'callback_url'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_open_api_app` ADD COLUMN `callback_url` VARCHAR(255) DEFAULT NULL COMMENT ''订单结果回调地址'' AFTER `allow_ip_list`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_open_api_app'
|
||||
AND COLUMN_NAME = 'callback_secret'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_open_api_app` ADD COLUMN `callback_secret` VARCHAR(128) DEFAULT NULL COMMENT ''订单结果回调签名密钥'' AFTER `callback_url`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_callback_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`open_api_app_id` BIGINT NOT NULL COMMENT '开放应用ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`order_id` BIGINT NOT NULL COMMENT '订单ID',
|
||||
`order_no` VARCHAR(64) NOT NULL COMMENT '订单号',
|
||||
`callback_url` VARCHAR(255) DEFAULT NULL COMMENT '回调地址',
|
||||
`event_type` VARCHAR(64) NOT NULL COMMENT '事件类型',
|
||||
`request_body` LONGTEXT DEFAULT NULL COMMENT '请求报文',
|
||||
`response_body` LONGTEXT DEFAULT NULL COMMENT '响应报文',
|
||||
`response_status` INT DEFAULT NULL COMMENT '响应HTTP状态码',
|
||||
`delivery_status` VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '投递状态(PENDING/SUCCESS/FAIL/SKIPPED)',
|
||||
`error_message` VARCHAR(500) DEFAULT NULL COMMENT '错误信息',
|
||||
`attempt_no` INT NOT NULL DEFAULT 1 COMMENT '尝试次数',
|
||||
`trigger_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '触发时间',
|
||||
`finish_time` DATETIME DEFAULT NULL COMMENT '完成时间',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_xxk_open_api_callback_log_app_id` (`open_api_app_id`),
|
||||
KEY `idx_xxk_open_api_callback_log_order_no` (`order_no`),
|
||||
KEY `idx_xxk_open_api_callback_log_status` (`delivery_status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放API订单回调日志表';
|
||||
@@ -0,0 +1,41 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 开放 API 审核菜单初始化
|
||||
-- 说明:
|
||||
-- 1. 在“代理平台”下新增“开放API审核”菜单。
|
||||
-- 2. 用于审核会员开放 API 申请,并授予管理员默认访问权限。
|
||||
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1220, 1100, '0,1100', '开放API审核', 'C', NULL, '/open-api-manage', 'Layout', NULL,
|
||||
1, 0, 1, 13, 'api', '/open-api-manage/open-api', NOW(), NOW(), NULL),
|
||||
(1221, 1220, '0,1100,1220', '开放API申请审核', 'M', 'ProxyOpenApiApply', 'open-api', 'proxy/open-api/index', NULL,
|
||||
0, 1, 1, 1, 'form', NULL, NOW(), NOW(), NULL),
|
||||
(122101, 1221, '0,1100,1220,1221', '开放API申请查询', 'B', NULL, '', NULL, 'openapi:apply:list',
|
||||
NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL),
|
||||
(122102, 1221, '0,1100,1220,1221', '开放API申请审核', 'B', NULL, '', NULL, 'openapi:apply:audit',
|
||||
NULL, NULL, 1, 2, '', NULL, NOW(), NOW(), NULL),
|
||||
(1222, 1220, '0,1100,1220', '开放API回调日志', 'M', 'ProxyOpenApiCallback', 'open-api-callback', 'proxy/open-api-callback/index', NULL,
|
||||
0, 1, 1, 2, 'notification', NULL, NOW(), NOW(), NULL),
|
||||
(122201, 1222, '0,1100,1220,1222', '开放API回调日志查询', 'B', NULL, '', NULL, 'openapi:callback-log:list',
|
||||
NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL),
|
||||
(122202, 1222, '0,1100,1220,1222', '开放API回调日志重发', 'B', NULL, '', NULL, 'openapi:callback-log:retry',
|
||||
NULL, NULL, 1, 2, '', NULL, NOW(), NOW(), NULL),
|
||||
(1223, 1220, '0,1100,1220', '开放API账户中心', 'M', 'ProxyOpenApiAccount', 'open-api-account', 'proxy/open-api-account/index', NULL,
|
||||
0, 1, 0, 3, 'wallet', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`update_time` = NOW();
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 1220), (2, 1221), (2, 122101), (2, 122102), (2, 1222), (2, 122201), (2, 122202), (2, 1223);
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
ALTER TABLE `xxk_product_static_country_price`
|
||||
DROP INDEX `uk_product_country`,
|
||||
ADD UNIQUE KEY `uk_product_static_price_region` (`product_id`, `region_id`);
|
||||
@@ -0,0 +1,17 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 分销配置演示数据
|
||||
-- 说明:
|
||||
-- 1. 仅当当前不存在启用中的分销配置时插入。
|
||||
-- 2. 佣金比例示例:一级 10%,二级 3%。
|
||||
|
||||
INSERT INTO `xxk_distribution_config`
|
||||
(`distribution_enabled`, `first_level_rate`, `second_level_rate`, `withdraw_threshold`, `withdraw_fee_rate`, `settle_rule`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`)
|
||||
SELECT 1, 0.1000, 0.0300, 0.00, 0.0000, 'OPEN_SUCCESS_AMOUNT', 1, '代理平台演示分销配置', 1, now(), 1, now()
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `xxk_distribution_config`
|
||||
WHERE `distribution_enabled` = 1
|
||||
AND `status` = 1
|
||||
);
|
||||
@@ -0,0 +1,292 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 代理平台菜单初始化
|
||||
-- 说明:
|
||||
-- 1. 本脚本只负责后台菜单、按钮权限和系统管理员授权。
|
||||
-- 2. 页面 component 先按约定路径预留,前端页面接入后直接对齐即可。
|
||||
|
||||
-- 一级目录:代理平台
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1100, 0, '0', '代理平台', 'C', 'Proxy', '/proxy', 'Layout', NULL,
|
||||
1, NULL, 1, 10, 'monitor', '/proxy/product', now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`always_show` = VALUES(`always_show`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`redirect` = VALUES(`redirect`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 商品管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1110, 1100, '0,1100', '商品管理', 'M', 'ProxyProduct', 'product', 'proxy/product/index', NULL,
|
||||
0, 1, 1, 1, 'goods', NULL, now(), now(), NULL),
|
||||
(11101, 1110, '0,1100,1110', '商品查询', 'B', NULL, '', NULL, 'proxy:product:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11102, 1110, '0,1100,1110', '商品新增', 'B', NULL, '', NULL, 'proxy:product:create',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(11103, 1110, '0,1100,1110', '商品修改', 'B', NULL, '', NULL, 'proxy:product:update',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL),
|
||||
(11104, 1110, '0,1100,1110', '商品删除', 'B', NULL, '', NULL, 'proxy:product:delete',
|
||||
NULL, NULL, 1, 4, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 静态价格管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1120, 1100, '0,1100', '静态价格管理', 'M', 'ProxyStaticPrice', 'static-price', 'proxy/static-price/index', NULL,
|
||||
0, 1, 1, 2, 'price-tag', NULL, now(), now(), NULL),
|
||||
(11201, 1120, '0,1100,1120', '静态价格查询', 'B', NULL, '', NULL, 'proxy:static-price:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11202, 1120, '0,1100,1120', '静态价格新增', 'B', NULL, '', NULL, 'proxy:static-price:create',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(11203, 1120, '0,1100,1120', '静态价格修改', 'B', NULL, '', NULL, 'proxy:static-price:update',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL),
|
||||
(11204, 1120, '0,1100,1120', '静态价格删除', 'B', NULL, '', NULL, 'proxy:static-price:delete',
|
||||
NULL, NULL, 1, 4, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 动态套餐管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1130, 1100, '0,1100', '动态套餐管理', 'M', 'ProxyDynamicPlan', 'dynamic-plan', 'proxy/dynamic-plan/index', NULL,
|
||||
0, 1, 1, 3, 'set-up', NULL, now(), now(), NULL),
|
||||
(11301, 1130, '0,1100,1130', '动态套餐查询', 'B', NULL, '', NULL, 'proxy:dynamic-plan:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11302, 1130, '0,1100,1130', '动态套餐新增', 'B', NULL, '', NULL, 'proxy:dynamic-plan:create',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(11303, 1130, '0,1100,1130', '动态套餐修改', 'B', NULL, '', NULL, 'proxy:dynamic-plan:update',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL),
|
||||
(11304, 1130, '0,1100,1130', '动态套餐删除', 'B', NULL, '', NULL, 'proxy:dynamic-plan:delete',
|
||||
NULL, NULL, 1, 4, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 时长倍率管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1140, 1100, '0,1100', '时长倍率管理', 'M', 'ProxyDurationMultiplier', 'duration-multiplier', 'proxy/duration-multiplier/index', NULL,
|
||||
0, 1, 1, 4, 'timer', NULL, now(), now(), NULL),
|
||||
(11401, 1140, '0,1100,1140', '时长倍率查询', 'B', NULL, '', NULL, 'proxy:duration:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11402, 1140, '0,1100,1140', '时长倍率新增', 'B', NULL, '', NULL, 'proxy:duration:create',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(11403, 1140, '0,1100,1140', '时长倍率修改', 'B', NULL, '', NULL, 'proxy:duration:update',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL),
|
||||
(11404, 1140, '0,1100,1140', '时长倍率删除', 'B', NULL, '', NULL, 'proxy:duration:delete',
|
||||
NULL, NULL, 1, 4, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 订单管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1150, 1100, '0,1100', '订单管理', 'M', 'ProxyOrder', 'order', 'proxy/order/index', NULL,
|
||||
0, 1, 1, 5, 'tickets', NULL, now(), now(), NULL),
|
||||
(11501, 1150, '0,1100,1150', '订单查询', 'B', NULL, '', NULL, 'proxy:order:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11502, 1150, '0,1100,1150', '订单创建', 'B', NULL, '', NULL, 'proxy:order:create',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(11503, 1150, '0,1100,1150', '订单支付', 'B', NULL, '', NULL, 'proxy:order:pay',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL),
|
||||
(11504, 1150, '0,1100,1150', '订单补偿重试', 'B', NULL, '', NULL, 'proxy:order:compensate',
|
||||
NULL, NULL, 1, 4, '', NULL, now(), now(), NULL),
|
||||
(11505, 1150, '0,1100,1150', '订单取消', 'B', NULL, '', NULL, 'proxy:order:cancel',
|
||||
NULL, NULL, 1, 5, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 静态代理管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1160, 1100, '0,1100', '静态代理管理', 'M', 'ProxyStaticAsset', 'static-asset', 'proxy/static-asset/index', NULL,
|
||||
0, 1, 1, 6, 'connection', NULL, now(), now(), NULL),
|
||||
(11601, 1160, '0,1100,1160', '静态代理查询', 'B', NULL, '', NULL, 'proxy:static-asset:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11602, 1160, '0,1100,1160', '静态代理续费', 'B', NULL, '', NULL, 'proxy:static-asset:renew',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(11603, 1160, '0,1100,1160', '静态代理维护', 'B', NULL, '', NULL, 'proxy:static-asset:update',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 动态通道管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1170, 1100, '0,1100', '动态通道管理', 'M', 'ProxyDynamicChannel', 'dynamic-channel', 'proxy/dynamic-channel/index', NULL,
|
||||
0, 1, 1, 7, 'share', NULL, now(), now(), NULL),
|
||||
(11701, 1170, '0,1100,1170', '动态通道查询', 'B', NULL, '', NULL, 'proxy:dynamic-channel:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11702, 1170, '0,1100,1170', '动态通道维护', 'B', NULL, '', NULL, 'proxy:dynamic-channel:update',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 上游供应商管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1180, 1100, '0,1100', '上游供应商管理', 'M', 'ProxyUpstreamProvider', 'upstream-provider', 'proxy/upstream-provider/index', NULL,
|
||||
0, 1, 1, 8, 'cpu', NULL, now(), now(), NULL),
|
||||
(11801, 1180, '0,1100,1180', '上游供应商查询', 'B', NULL, '', NULL, 'proxy:upstream:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11802, 1180, '0,1100,1180', '上游供应商新增', 'B', NULL, '', NULL, 'proxy:upstream:create',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(11803, 1180, '0,1100,1180', '上游供应商修改', 'B', NULL, '', NULL, 'proxy:upstream:update',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL),
|
||||
(11804, 1180, '0,1100,1180', '上游供应商删除', 'B', NULL, '', NULL, 'proxy:upstream:delete',
|
||||
NULL, NULL, 1, 4, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 钱包管理
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1190, 1100, '0,1100', '钱包管理', 'M', 'ProxyWallet', 'wallet', 'proxy/wallet/index', NULL,
|
||||
0, 1, 1, 9, 'wallet', NULL, now(), now(), NULL),
|
||||
(11901, 1190, '0,1100,1190', '钱包查询', 'B', NULL, '', NULL, 'proxy:wallet:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(11902, 1190, '0,1100,1190', '钱包加款', 'B', NULL, '', NULL, 'proxy:wallet:recharge',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(11910, 1190, '0,1100,1190', '充值订单', 'C', 'ProxyWalletRecharge', 'wallet-recharge', 'proxy/wallet-recharge/index', NULL,
|
||||
0, 1, 1, 3, 'wallet', NULL, now(), now(), NULL),
|
||||
(11911, 11910, '0,1100,1190,11910', '充值订单查询', 'B', NULL, '', NULL, 'proxy:wallet:recharge-order:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 分销管理(页面和接口后续继续补齐)
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1200, 1100, '0,1100', '分销管理', 'M', 'ProxyDistribution', 'distribution', 'proxy/distribution/index', NULL,
|
||||
0, 1, 1, 10, 'user-filled', NULL, now(), now(), NULL),
|
||||
(12001, 1200, '0,1100,1200', '分销配置查询', 'B', NULL, '', NULL, 'proxy:distribution:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL),
|
||||
(12002, 1200, '0,1100,1200', '分销配置维护', 'B', NULL, '', NULL, 'proxy:distribution:update',
|
||||
NULL, NULL, 1, 2, '', NULL, now(), now(), NULL),
|
||||
(12003, 1200, '0,1100,1200', '佣金流水查询', 'B', NULL, '', NULL, 'proxy:distribution:commission:list',
|
||||
NULL, NULL, 1, 3, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
|
||||
-- 系统管理员授权
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 1100),
|
||||
(2, 1110), (2, 11101), (2, 11102), (2, 11103), (2, 11104),
|
||||
(2, 1120), (2, 11201), (2, 11202), (2, 11203), (2, 11204),
|
||||
(2, 1130), (2, 11301), (2, 11302), (2, 11303), (2, 11304),
|
||||
(2, 1140), (2, 11401), (2, 11402), (2, 11403), (2, 11404),
|
||||
(2, 1150), (2, 11501), (2, 11502), (2, 11503), (2, 11504), (2, 11505),
|
||||
(2, 1160), (2, 11601), (2, 11602), (2, 11603),
|
||||
(2, 1170), (2, 11701), (2, 11702),
|
||||
(2, 1180), (2, 11801), (2, 11802), (2, 11803), (2, 11804),
|
||||
(2, 1190), (2, 11901), (2, 11902),
|
||||
(2, 1200), (2, 12001), (2, 12002), (2, 12003);
|
||||
@@ -0,0 +1,21 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 订单待支付取消能力升级脚本
|
||||
-- 说明:
|
||||
-- 1. 新增“订单取消”按钮权限,允许后台直接取消待支付订单。
|
||||
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(11505, 1150, '0,1100,1150', '订单取消', 'B', NULL, '', NULL, 'proxy:order:cancel',
|
||||
NULL, NULL, 1, 5, '', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = NOW();
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 11505);
|
||||
@@ -0,0 +1,29 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 订单补偿记录菜单升级脚本
|
||||
-- 说明:
|
||||
-- 1. 在代理平台下新增“订单补偿记录”菜单。
|
||||
-- 2. 用于查看订单补偿尝试历史、失败原因和关联退款流水。
|
||||
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1210, 1100, '0,1100', '订单补偿记录', 'M', 'ProxyOrderCompensationRecord', 'order-compensation-record', 'proxy/order-compensation-record/index', NULL,
|
||||
0, 1, 1, 12, 'warning', NULL, NOW(), NOW(), NULL),
|
||||
(12101, 1210, '0,1100,1210', '订单补偿记录查询', 'B', NULL, '', NULL, 'proxy:order-compensation-record:list',
|
||||
NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`update_time` = NOW();
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 1210), (2, 12101);
|
||||
@@ -0,0 +1,52 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 订单异常补偿闭环升级脚本
|
||||
-- 说明:
|
||||
-- 1. 为订单主表补充补偿状态、补偿说明、重试次数和最近补偿时间。
|
||||
-- 2. 新增订单补偿记录表,保留每次退款补偿尝试的结果。
|
||||
-- 3. 新增“订单补偿重试”按钮权限,方便后台人工重试失败补偿。
|
||||
|
||||
ALTER TABLE `xxk_proxy_order`
|
||||
ADD COLUMN `compensation_status` VARCHAR(32) NOT NULL DEFAULT 'NONE' COMMENT '补偿状态(NONE无需补偿 PENDING待补偿 SUCCESS补偿成功 FAIL补偿失败)' AFTER `open_status`,
|
||||
ADD COLUMN `compensation_reason` VARCHAR(255) NULL COMMENT '补偿说明' AFTER `remark`,
|
||||
ADD COLUMN `compensation_retry_count` INT NOT NULL DEFAULT 0 COMMENT '补偿重试次数' AFTER `compensation_reason`,
|
||||
ADD COLUMN `last_compensation_time` DATETIME NULL COMMENT '最近补偿时间' AFTER `compensation_retry_count`,
|
||||
ADD KEY `idx_xxk_proxy_order_compensation_status` (`compensation_status`, `update_time`);
|
||||
|
||||
CREATE TABLE `xxk_order_compensation_record` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`order_id` BIGINT NOT NULL COMMENT '订单ID',
|
||||
`order_no` VARCHAR(64) NOT NULL COMMENT '订单号',
|
||||
`compensation_type` VARCHAR(32) NOT NULL COMMENT '补偿类型',
|
||||
`attempt_no` INT NOT NULL DEFAULT 1 COMMENT '第几次补偿尝试',
|
||||
`compensation_status` VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '补偿状态(PENDING/SUCCESS/FAIL)',
|
||||
`reason` VARCHAR(255) DEFAULT NULL COMMENT '补偿原因',
|
||||
`error_message` VARCHAR(500) DEFAULT NULL COMMENT '失败原因',
|
||||
`related_flow_no` VARCHAR(64) DEFAULT NULL COMMENT '关联退款流水号',
|
||||
`operator_id` BIGINT DEFAULT NULL COMMENT '操作人ID',
|
||||
`operator_name` VARCHAR(100) DEFAULT NULL COMMENT '操作人名称',
|
||||
`execute_time` DATETIME DEFAULT NULL COMMENT '执行时间',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_order_compensation_attempt` (`order_id`, `compensation_type`, `attempt_no`),
|
||||
KEY `idx_xxk_order_compensation_order_no` (`order_no`),
|
||||
KEY `idx_xxk_order_compensation_status` (`compensation_status`, `execute_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单补偿记录表';
|
||||
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(11504, 1150, '0,1100,1150', '订单补偿重试', 'B', NULL, '', NULL, 'proxy:order:compensate',
|
||||
NULL, NULL, 1, 4, '', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = NOW();
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 11504);
|
||||
@@ -0,0 +1,50 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 订单支付对外适配升级脚本
|
||||
-- 说明:
|
||||
-- 1. 新增订单支付记录表,承接支付中台 / 自研支付的统一支付单。
|
||||
-- 2. 新增支付回调日志表,保留异步通知原文和处理结果。
|
||||
-- 3. 当前主订单表已存在 payment_type 字段,本脚本不重复新增。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_proxy_order_pay` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`pay_order_no` VARCHAR(64) NOT NULL COMMENT '支付单号',
|
||||
`order_id` BIGINT NOT NULL COMMENT '业务订单ID',
|
||||
`order_no` VARCHAR(64) NOT NULL COMMENT '业务订单号',
|
||||
`payment_type` VARCHAR(32) NOT NULL COMMENT '支付方式(BALANCE/ALIPAY/WECHAT/EXTERNAL)',
|
||||
`pay_status` VARCHAR(32) NOT NULL DEFAULT 'INIT' COMMENT '支付单状态(INIT/PAYING/PAID/FAIL/CLOSED/REFUNDED)',
|
||||
`channel_order_no` VARCHAR(128) DEFAULT NULL COMMENT '渠道支付单号',
|
||||
`channel_response` TEXT DEFAULT NULL COMMENT '渠道原始响应',
|
||||
`client_type` VARCHAR(32) DEFAULT NULL COMMENT '客户端类型(PC/H5/APP/MINI_PROGRAM)',
|
||||
`return_url` VARCHAR(500) DEFAULT NULL COMMENT '支付完成跳转地址',
|
||||
`amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '支付金额',
|
||||
`currency` VARCHAR(16) DEFAULT NULL COMMENT '币种',
|
||||
`paid_time` DATETIME DEFAULT NULL COMMENT '支付成功时间',
|
||||
`expire_time` DATETIME DEFAULT NULL COMMENT '支付过期时间',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_proxy_order_pay_no` (`pay_order_no`),
|
||||
KEY `idx_xxk_proxy_order_pay_order_no` (`order_no`),
|
||||
KEY `idx_xxk_proxy_order_pay_status` (`pay_status`, `update_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单支付记录表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_proxy_payment_notify_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`pay_order_no` VARCHAR(64) DEFAULT NULL COMMENT '支付单号',
|
||||
`order_no` VARCHAR(64) DEFAULT NULL COMMENT '业务订单号',
|
||||
`payment_type` VARCHAR(32) DEFAULT NULL COMMENT '支付方式',
|
||||
`notify_type` VARCHAR(32) DEFAULT NULL COMMENT '通知类型(PAY/REFUND/CLOSE)',
|
||||
`notify_body` MEDIUMTEXT DEFAULT NULL COMMENT '通知原文',
|
||||
`verify_status` VARCHAR(16) NOT NULL DEFAULT 'INIT' COMMENT '验签状态(INIT/SUCCESS/FAIL)',
|
||||
`process_status` VARCHAR(16) NOT NULL DEFAULT 'INIT' COMMENT '处理状态(INIT/SUCCESS/FAIL)',
|
||||
`error_message` VARCHAR(500) DEFAULT NULL COMMENT '失败原因',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_xxk_proxy_payment_notify_order_no` (`order_no`),
|
||||
KEY `idx_xxk_proxy_payment_notify_status` (`verify_status`, `process_status`, `update_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付回调日志表';
|
||||
@@ -0,0 +1,541 @@
|
||||
# XXK 代理平台数据库(MySQL 5.7 ~ MySQL 8.x)
|
||||
# 说明:
|
||||
# 1. 该 SQL 设计为导入当前 youlai_admin 库
|
||||
# 2. 业务表统一使用 xxk_ 前缀
|
||||
# 3. 一期支持静态住宅 IP、动态住宅 IP、余额支付、二级分销
|
||||
|
||||
USE youlai_admin;
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_upstream_provider
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_upstream_provider`;
|
||||
CREATE TABLE `xxk_upstream_provider` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`provider_code` varchar(64) NOT NULL COMMENT '供应商编码',
|
||||
`provider_name` varchar(100) NOT NULL COMMENT '供应商名称',
|
||||
`provider_type` varchar(32) NOT NULL COMMENT '供应商类型',
|
||||
`base_url` varchar(255) NOT NULL COMMENT '接口基础地址',
|
||||
`auth_user_id` varchar(100) DEFAULT NULL COMMENT '上游UserId',
|
||||
`auth_token` varchar(255) DEFAULT NULL COMMENT '上游Token',
|
||||
`success_codes` varchar(100) DEFAULT '1000,2001' COMMENT '成功业务码集合',
|
||||
`http_timeout_ms` int DEFAULT 10000 COMMENT '超时时间(毫秒)',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-启用 0-禁用)',
|
||||
`is_default` tinyint DEFAULT 0 COMMENT '默认供应商(1-是 0-否)',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_provider_code` (`provider_code`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上游供应商配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_product
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_product`;
|
||||
CREATE TABLE `xxk_product` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`product_code` varchar(64) NOT NULL COMMENT '商品编码',
|
||||
`product_name` varchar(100) NOT NULL COMMENT '商品名称',
|
||||
`product_type` varchar(32) NOT NULL COMMENT '商品类型(STATIC_RESIDENTIAL/DYNAMIC_RESIDENTIAL)',
|
||||
`upstream_provider_id` bigint NOT NULL COMMENT '上游供应商ID',
|
||||
`upstream_product_code` varchar(64) DEFAULT NULL COMMENT '上游商品编码',
|
||||
`proxies_type` varchar(64) DEFAULT NULL COMMENT '上游代理类型',
|
||||
`proxies_format` varchar(64) DEFAULT NULL COMMENT '上游代理格式',
|
||||
`purpose_web` varchar(64) DEFAULT NULL COMMENT '上游用途参数',
|
||||
`upstream_params` text DEFAULT NULL COMMENT '上游扩展参数JSON',
|
||||
`upstream_capabilities` text DEFAULT NULL COMMENT '上游能力JSON',
|
||||
`protocols_type` tinyint DEFAULT NULL COMMENT '协议类型(1-HTTP 2-SOCKS5 3-HTTP+SOCKS5)',
|
||||
`udp_status` tinyint DEFAULT 0 COMMENT '是否启用UDP(1-是 0-否)',
|
||||
`currency` varchar(10) DEFAULT 'RMB' COMMENT '币种',
|
||||
`enable_distribution` tinyint DEFAULT 1 COMMENT '是否参与分销(1-是 0-否)',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-上架 0-下架)',
|
||||
`sort` int DEFAULT 0 COMMENT '排序',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_product_code` (`product_code`) USING BTREE,
|
||||
KEY `idx_product_type_status` (`product_type`, `status`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品主表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_product_static_country_price
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_product_static_country_price`;
|
||||
CREATE TABLE `xxk_product_static_country_price` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`product_id` bigint NOT NULL COMMENT '商品ID',
|
||||
`price_type` varchar(20) DEFAULT NULL COMMENT '价格类型(DEFAULT默认/NODE特殊节点)',
|
||||
`qiyun_product_type` varchar(32) DEFAULT NULL COMMENT '齐云产品类型',
|
||||
`qiyun_pid` varchar(64) DEFAULT NULL COMMENT '齐云项目ID',
|
||||
`qiyun_project_name` varchar(255) DEFAULT NULL COMMENT '齐云项目名称',
|
||||
`qiyun_area_id` varchar(64) DEFAULT NULL COMMENT '齐云省份ID',
|
||||
`qiyun_area_name` varchar(255) DEFAULT NULL COMMENT '齐云省份名称',
|
||||
`qiyun_node_id` varchar(100) DEFAULT NULL COMMENT '齐云节点ID',
|
||||
`qiyun_node_name` varchar(255) DEFAULT NULL COMMENT '齐云节点名称',
|
||||
`region_id` bigint DEFAULT NULL COMMENT '地区ID',
|
||||
`region_code` varchar(64) DEFAULT NULL COMMENT '地区编号',
|
||||
`region_name` varchar(100) DEFAULT NULL COMMENT '地区名称',
|
||||
`region_name_zh` varchar(100) DEFAULT NULL COMMENT '地区中文名',
|
||||
`country_code` varchar(10) NOT NULL COMMENT '国家编码',
|
||||
`country_name` varchar(100) NOT NULL COMMENT '国家名称',
|
||||
`base_price` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '地区基础单价',
|
||||
`currency` varchar(10) DEFAULT 'RMB' COMMENT '币种',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-启用 0-禁用)',
|
||||
`sort` int DEFAULT 0 COMMENT '排序',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_product_country` (`product_id`, `country_code`) USING BTREE,
|
||||
KEY `idx_static_price_qiyun_node` (`product_id`, `price_type`, `qiyun_node_id`) USING BTREE,
|
||||
KEY `idx_country_code` (`country_code`) USING BTREE,
|
||||
KEY `idx_static_price_region_id` (`region_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='静态商品地区定价表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_product_duration_multiplier
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_product_duration_multiplier`;
|
||||
CREATE TABLE `xxk_product_duration_multiplier` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`product_id` bigint NOT NULL COMMENT '商品ID',
|
||||
`duration_days` int NOT NULL COMMENT '兼容时长天数',
|
||||
`duration_unit` varchar(16) NOT NULL DEFAULT 'DAY' COMMENT '时长单位(DAY天/HOUR小时)',
|
||||
`duration_value` int NOT NULL DEFAULT 1 COMMENT '时长数值',
|
||||
`multiplier` decimal(10,4) NOT NULL DEFAULT 1.0000 COMMENT '价格倍率',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-启用 0-禁用)',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_product_duration_unit_value` (`product_id`, `duration_unit`, `duration_value`) USING BTREE,
|
||||
KEY `idx_product_duration_days` (`product_id`, `duration_days`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品时长倍率表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_product_dynamic_plan
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_product_dynamic_plan`;
|
||||
CREATE TABLE `xxk_product_dynamic_plan` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`product_id` bigint NOT NULL COMMENT '商品ID',
|
||||
`plan_code` varchar(64) NOT NULL COMMENT '流量套餐编码',
|
||||
`flow_gb` decimal(12,3) NOT NULL DEFAULT 0.000 COMMENT '流量套餐GB',
|
||||
`base_price` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '流量套餐基础价',
|
||||
`allow_sticky_session` tinyint DEFAULT 1 COMMENT '允许粘性会话(1-是 0-否)',
|
||||
`allow_location_select` tinyint DEFAULT 1 COMMENT '允许地区选择(1-是 0-否)',
|
||||
`allow_custom_limit` tinyint DEFAULT 0 COMMENT '允许自定义通道流量上限(1-是 0-否)',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-启用 0-禁用)',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_plan_code` (`plan_code`) USING BTREE,
|
||||
UNIQUE KEY `uk_product_flow` (`product_id`, `flow_gb`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='动态商品流量套餐表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_user_profile
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_user_profile`;
|
||||
CREATE TABLE `xxk_user_profile` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_id` bigint NOT NULL COMMENT '关联sys_user.id',
|
||||
`user_type` varchar(32) DEFAULT 'CUSTOMER' COMMENT '用户类型',
|
||||
`invite_code` varchar(32) DEFAULT NULL COMMENT '邀请码',
|
||||
`register_source` varchar(32) DEFAULT 'ADMIN' COMMENT '注册来源',
|
||||
`register_ip` varchar(64) DEFAULT NULL COMMENT '注册IP',
|
||||
`last_login_ip` varchar(64) DEFAULT NULL COMMENT '最后登录IP',
|
||||
`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_user_id` (`user_id`) USING BTREE,
|
||||
UNIQUE KEY `uk_invite_code` (`invite_code`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户业务扩展表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_wallet_account
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_wallet_account`;
|
||||
CREATE TABLE `xxk_wallet_account` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`balance` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '可用余额',
|
||||
`frozen_balance` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '冻结余额',
|
||||
`total_recharge_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计充值金额',
|
||||
`total_consume_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计消费金额',
|
||||
`total_refund_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计退款金额',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_wallet_user_id` (`user_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='钱包账户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_wallet_flow
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_wallet_flow`;
|
||||
CREATE TABLE `xxk_wallet_flow` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`flow_no` varchar(64) NOT NULL COMMENT '流水号',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`biz_type` varchar(32) NOT NULL COMMENT '业务类型',
|
||||
`change_type` varchar(16) NOT NULL COMMENT '变动类型(IN/OUT)',
|
||||
`change_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动金额',
|
||||
`before_balance` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动前余额',
|
||||
`after_balance` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动后余额',
|
||||
`related_order_no` varchar(64) DEFAULT NULL COMMENT '关联订单号',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`operate_by` bigint DEFAULT NULL COMMENT '操作人ID',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_flow_no` (`flow_no`) USING BTREE,
|
||||
KEY `idx_wallet_user_time` (`user_id`, `create_time`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='钱包流水表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_proxy_order
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_proxy_order`;
|
||||
CREATE TABLE `xxk_proxy_order` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`order_no` varchar(64) NOT NULL COMMENT '订单号',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`product_id` bigint NOT NULL COMMENT '商品ID',
|
||||
`product_type` varchar(32) NOT NULL COMMENT '商品类型',
|
||||
`order_type` varchar(16) NOT NULL DEFAULT 'NEW' COMMENT '订单类型(NEW/RENEW)',
|
||||
`order_status` varchar(32) NOT NULL DEFAULT 'WAIT_PAY' COMMENT '订单状态',
|
||||
`pay_status` varchar(32) NOT NULL DEFAULT 'UNPAID' COMMENT '支付状态',
|
||||
`open_status` varchar(32) NOT NULL DEFAULT 'WAIT_OPEN' COMMENT '开通状态',
|
||||
`payment_type` varchar(16) DEFAULT 'BALANCE' COMMENT '支付方式',
|
||||
`currency` varchar(10) DEFAULT 'RMB' COMMENT '币种',
|
||||
`buy_quantity` int NOT NULL DEFAULT 1 COMMENT '购买数量',
|
||||
`duration_days` int NOT NULL COMMENT '购买天数',
|
||||
`sale_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '销售金额',
|
||||
`paid_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '实付金额',
|
||||
`cost_amount` decimal(18,2) DEFAULT 0.00 COMMENT '采购成本',
|
||||
`open_success_amount` decimal(18,2) DEFAULT 0.00 COMMENT '开通成功金额',
|
||||
`region_snapshot` text COMMENT '地区快照',
|
||||
`product_snapshot` mediumtext COMMENT '商品快照',
|
||||
`inviter_user_id` bigint DEFAULT NULL COMMENT '一级邀请人',
|
||||
`parent_inviter_user_id` bigint DEFAULT NULL COMMENT '二级邀请人',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`pay_time` datetime DEFAULT NULL COMMENT '支付时间',
|
||||
`open_time` datetime DEFAULT NULL COMMENT '开通时间',
|
||||
`finish_time` datetime DEFAULT NULL COMMENT '完成时间',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_order_no` (`order_no`) USING BTREE,
|
||||
KEY `idx_order_user_time` (`user_id`, `create_time`) USING BTREE,
|
||||
KEY `idx_order_status` (`order_status`, `pay_status`, `open_status`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代理订单主表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_proxy_order_item
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_proxy_order_item`;
|
||||
CREATE TABLE `xxk_proxy_order_item` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`order_no` varchar(64) NOT NULL COMMENT '订单号',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`product_id` bigint NOT NULL COMMENT '商品ID',
|
||||
`product_type` varchar(32) NOT NULL COMMENT '商品类型',
|
||||
`country_code` varchar(10) DEFAULT NULL COMMENT '国家编码',
|
||||
`country_name` varchar(100) DEFAULT NULL COMMENT '国家名称',
|
||||
`quantity` int NOT NULL DEFAULT 1 COMMENT '数量',
|
||||
`duration_days` int NOT NULL COMMENT '时长天数',
|
||||
`duration_multiplier` decimal(10,4) NOT NULL DEFAULT 1.0000 COMMENT '时长倍率',
|
||||
`unit_price` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '单价',
|
||||
`line_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '小计金额',
|
||||
`flow_gb` decimal(12,3) DEFAULT NULL COMMENT '动态流量GB',
|
||||
`upstream_payload` mediumtext COMMENT '上游请求快照',
|
||||
`item_status` varchar(32) DEFAULT 'INIT' COMMENT '订单项状态',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_order_item_order_id` (`order_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代理订单项表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_static_proxy_asset
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_static_proxy_asset`;
|
||||
CREATE TABLE `xxk_static_proxy_asset` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`order_item_id` bigint DEFAULT NULL COMMENT '订单项ID',
|
||||
`product_id` bigint NOT NULL COMMENT '商品ID',
|
||||
`upstream_provider_id` bigint NOT NULL COMMENT '上游供应商ID',
|
||||
`upstream_order_no` varchar(64) DEFAULT NULL COMMENT '上游订单号',
|
||||
`upstream_proxy_id` varchar(64) NOT NULL COMMENT '上游代理ID',
|
||||
`proxy_address` varchar(100) NOT NULL COMMENT '代理IP',
|
||||
`port` int NOT NULL COMMENT '端口',
|
||||
`username` varchar(100) DEFAULT NULL COMMENT '账号',
|
||||
`password` varchar(100) DEFAULT NULL COMMENT '密码',
|
||||
`protocols` varchar(32) DEFAULT NULL COMMENT '协议',
|
||||
`country_code` varchar(10) DEFAULT NULL COMMENT '国家编码',
|
||||
`country_name` varchar(100) DEFAULT NULL COMMENT '国家名称',
|
||||
`city_name` varchar(100) DEFAULT NULL COMMENT '城市名称',
|
||||
`proxy_status` tinyint DEFAULT 1 COMMENT '代理状态(1-正常 2-禁用 3-维护)',
|
||||
`is_auto_renew` tinyint DEFAULT 0 COMMENT '自动续费(1-开 0-关)',
|
||||
`expired_at` datetime DEFAULT NULL COMMENT '到期时间',
|
||||
`last_sync_time` datetime DEFAULT NULL COMMENT '最后同步时间',
|
||||
`raw_data` mediumtext COMMENT '原始数据快照',
|
||||
`status` tinyint DEFAULT 1 COMMENT '业务状态(1-有效 0-无效)',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_static_upstream_proxy_id` (`upstream_proxy_id`) USING BTREE,
|
||||
KEY `idx_static_user_status` (`user_id`, `status`) USING BTREE,
|
||||
KEY `idx_static_expired_at` (`expired_at`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='静态代理资产表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_static_proxy_whitelist
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_static_proxy_whitelist`;
|
||||
CREATE TABLE `xxk_static_proxy_whitelist` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`product_id` bigint DEFAULT NULL COMMENT '商品ID',
|
||||
`proxy_type` varchar(64) DEFAULT NULL COMMENT '静态代理类型',
|
||||
`whitelist_ip` varchar(64) NOT NULL COMMENT '白名单IP',
|
||||
`upstream_address_id` varchar(64) DEFAULT NULL COMMENT '上游白名单记录ID',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-有效 0-删除)',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_user_whitelist_ip` (`user_id`, `whitelist_ip`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='静态代理白名单表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_static_proxy_change_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_static_proxy_change_log`;
|
||||
CREATE TABLE `xxk_static_proxy_change_log` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`asset_id` bigint NOT NULL COMMENT '静态资产ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`change_type` varchar(32) NOT NULL COMMENT '变更类型',
|
||||
`before_value` text COMMENT '变更前',
|
||||
`after_value` text COMMENT '变更后',
|
||||
`operator_id` bigint DEFAULT NULL COMMENT '操作人ID',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_static_change_asset_id` (`asset_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='静态代理变更日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_dynamic_channel
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_dynamic_channel`;
|
||||
CREATE TABLE `xxk_dynamic_channel` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`order_item_id` bigint DEFAULT NULL COMMENT '订单项ID',
|
||||
`product_id` bigint NOT NULL COMMENT '商品ID',
|
||||
`upstream_provider_id` bigint NOT NULL COMMENT '上游供应商ID',
|
||||
`upstream_channel_id` varchar(64) NOT NULL COMMENT '上游通道ID',
|
||||
`channel_name` varchar(100) NOT NULL COMMENT '通道名称',
|
||||
`channel_password` varchar(100) DEFAULT NULL COMMENT '通道密码',
|
||||
`channel_status` varchar(16) NOT NULL DEFAULT 'ENABLED' COMMENT '通道状态',
|
||||
`flow_package_gb` decimal(12,3) NOT NULL DEFAULT 0.000 COMMENT '购买流量包GB',
|
||||
`traffic_limit_gb` decimal(12,3) NOT NULL DEFAULT 0.000 COMMENT '通道流量上限GB',
|
||||
`used_traffic_gb` decimal(12,3) NOT NULL DEFAULT 0.000 COMMENT '已用流量GB',
|
||||
`remaining_traffic_gb` decimal(12,3) NOT NULL DEFAULT 0.000 COMMENT '剩余流量GB',
|
||||
`expired_at` datetime DEFAULT NULL COMMENT '到期时间',
|
||||
`allow_generate_proxy` tinyint DEFAULT 1 COMMENT '允许生成代理(1-是 0-否)',
|
||||
`last_sync_time` datetime DEFAULT NULL COMMENT '最后同步时间',
|
||||
`raw_data` mediumtext COMMENT '原始数据快照',
|
||||
`status` tinyint DEFAULT 1 COMMENT '业务状态(1-有效 0-无效)',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_dynamic_upstream_channel_id` (`upstream_channel_id`) USING BTREE,
|
||||
KEY `idx_dynamic_user_status` (`user_id`, `status`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='动态通道表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_dynamic_channel_traffic_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_dynamic_channel_traffic_log`;
|
||||
CREATE TABLE `xxk_dynamic_channel_traffic_log` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`channel_id` bigint NOT NULL COMMENT '动态通道ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`date_type` tinyint DEFAULT NULL COMMENT '查询日期类型',
|
||||
`start_date` date DEFAULT NULL COMMENT '开始日期',
|
||||
`end_date` date DEFAULT NULL COMMENT '结束日期',
|
||||
`used_traffic_gb` decimal(12,3) NOT NULL DEFAULT 0.000 COMMENT '已用流量GB',
|
||||
`total_traffic_gb` decimal(12,3) DEFAULT 0.000 COMMENT '累计流量GB',
|
||||
`raw_data` mediumtext COMMENT '原始返回',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_channel_traffic_channel_id` (`channel_id`, `create_time`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='动态通道流量日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_dynamic_proxy_generate_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_dynamic_proxy_generate_log`;
|
||||
CREATE TABLE `xxk_dynamic_proxy_generate_log` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`channel_id` bigint NOT NULL COMMENT '动态通道ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`location` varchar(32) NOT NULL COMMENT '地区参数',
|
||||
`domain` varchar(32) DEFAULT 'Global' COMMENT '域名参数',
|
||||
`sticky_session_time` int NOT NULL DEFAULT 0 COMMENT '粘性会话分钟',
|
||||
`proxy_count` int NOT NULL DEFAULT 1 COMMENT '生成数量',
|
||||
`state` varchar(100) DEFAULT NULL COMMENT '州',
|
||||
`city` varchar(100) DEFAULT NULL COMMENT '城市',
|
||||
`generated_result` mediumtext COMMENT '生成结果',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_generate_channel_id` (`channel_id`, `create_time`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='动态代理生成日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_distribution_relation
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_distribution_relation`;
|
||||
CREATE TABLE `xxk_distribution_relation` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_id` bigint DEFAULT NULL COMMENT '当前用户ID',
|
||||
`parent_user_id` bigint DEFAULT NULL COMMENT '一级上级用户ID',
|
||||
`grand_parent_user_id` bigint DEFAULT NULL COMMENT '二级上级用户ID',
|
||||
`bind_source` varchar(32) DEFAULT NULL COMMENT '绑定来源',
|
||||
`bind_time` datetime DEFAULT NULL COMMENT '绑定时间',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_distribution_user_id` (`user_id`) USING BTREE,
|
||||
KEY `idx_distribution_parent_user_id` (`parent_user_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销关系表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_distribution_config
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_distribution_config`;
|
||||
CREATE TABLE `xxk_distribution_config` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`distribution_enabled` tinyint DEFAULT 0 COMMENT '是否启用分销(1-是 0-否)',
|
||||
`first_level_rate` decimal(10,4) NOT NULL DEFAULT 0.0000 COMMENT '一级佣金比例',
|
||||
`second_level_rate` decimal(10,4) NOT NULL DEFAULT 0.0000 COMMENT '二级佣金比例',
|
||||
`withdraw_threshold` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '提现门槛金额',
|
||||
`withdraw_fee_rate` decimal(10,4) NOT NULL DEFAULT 0.0000 COMMENT '提现手续费比例',
|
||||
`settle_rule` varchar(32) DEFAULT 'OPEN_SUCCESS_AMOUNT' COMMENT '结算规则',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-启用 0-禁用)',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_distribution_commission
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_distribution_commission`;
|
||||
CREATE TABLE `xxk_distribution_commission` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_id` bigint NOT NULL COMMENT '佣金归属用户ID',
|
||||
`from_user_id` bigint NOT NULL COMMENT '消费用户ID',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`order_no` varchar(64) NOT NULL COMMENT '订单号',
|
||||
`level_no` tinyint NOT NULL COMMENT '层级(1/2)',
|
||||
`rate` decimal(10,4) NOT NULL DEFAULT 0.0000 COMMENT '佣金比例',
|
||||
`base_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '佣金基数',
|
||||
`commission_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '佣金金额',
|
||||
`commission_status` varchar(32) NOT NULL DEFAULT 'PENDING' COMMENT '佣金状态',
|
||||
`confirm_time` datetime DEFAULT NULL COMMENT '确认时间',
|
||||
`settle_time` datetime DEFAULT NULL COMMENT '结算时间',
|
||||
`invalid_time` datetime DEFAULT NULL COMMENT '失效时间',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_commission_user_status` (`user_id`, `commission_status`) USING BTREE,
|
||||
KEY `idx_commission_order_id` (`order_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销佣金流水表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_upstream_request_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_upstream_request_log`;
|
||||
CREATE TABLE `xxk_upstream_request_log` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`provider_id` bigint NOT NULL COMMENT '供应商ID',
|
||||
`biz_type` varchar(32) NOT NULL COMMENT '业务类型',
|
||||
`api_name` varchar(100) NOT NULL COMMENT '接口名称',
|
||||
`request_url` varchar(255) NOT NULL COMMENT '请求地址',
|
||||
`request_headers` mediumtext COMMENT '请求头',
|
||||
`request_body` longtext COMMENT '请求体',
|
||||
`response_body` longtext COMMENT '响应体',
|
||||
`http_status` int DEFAULT NULL COMMENT 'HTTP状态码',
|
||||
`biz_code` varchar(32) DEFAULT NULL COMMENT '业务码',
|
||||
`success_flag` tinyint DEFAULT 0 COMMENT '是否成功(1-是 0-否)',
|
||||
`duration_ms` int DEFAULT NULL COMMENT '耗时毫秒',
|
||||
`error_message` varchar(500) DEFAULT NULL COMMENT '异常信息',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_request_provider_time` (`provider_id`, `create_time`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上游请求日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for xxk_order_operate_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `xxk_order_operate_log`;
|
||||
CREATE TABLE `xxk_order_operate_log` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`order_no` varchar(64) NOT NULL COMMENT '订单号',
|
||||
`operate_type` varchar(32) NOT NULL COMMENT '操作类型',
|
||||
`before_status` varchar(64) DEFAULT NULL COMMENT '操作前状态',
|
||||
`after_status` varchar(64) DEFAULT NULL COMMENT '操作后状态',
|
||||
`content` text COMMENT '操作内容',
|
||||
`operator_id` bigint DEFAULT NULL COMMENT '操作人ID',
|
||||
`operator_name` varchar(100) DEFAULT NULL COMMENT '操作人名称',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_order_operate_order_id` (`order_id`, `create_time`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单操作日志表';
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
@@ -0,0 +1,627 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 齐云 IP 上游适配升级脚本
|
||||
|
||||
SET @schema_name = DATABASE();
|
||||
|
||||
-- 兼容未执行会员代理归属升级脚本的数据库。
|
||||
-- 当前 StaticProxyAsset 实体已包含 memberUserId,MyBatis-Plus 查询会选择 member_user_id;
|
||||
-- 老库缺少该字段时,静态代理自动续费/同步定时任务会报 Unknown column。
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_static_proxy_asset'
|
||||
AND COLUMN_NAME = 'member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_static_proxy_asset` ADD COLUMN `member_user_id` BIGINT NULL COMMENT ''会员ID'' AFTER `user_id`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_static_proxy_asset'
|
||||
AND INDEX_NAME = 'idx_xxk_static_proxy_asset_member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_static_proxy_asset` ADD INDEX `idx_xxk_static_proxy_asset_member_user_id` (`member_user_id`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 会员侧订单开通会写入 member_user_id;这些表如果仍是旧结构,会导致上游已开通但本地资源/白名单插入失败。
|
||||
ALTER TABLE `xxk_dynamic_channel` MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_dynamic_channel'
|
||||
AND COLUMN_NAME = 'member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_dynamic_channel` ADD COLUMN `member_user_id` BIGINT NULL COMMENT ''会员ID'' AFTER `user_id`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_dynamic_channel'
|
||||
AND INDEX_NAME = 'idx_xxk_dynamic_channel_member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_dynamic_channel` ADD INDEX `idx_xxk_dynamic_channel_member_user_id` (`member_user_id`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `xxk_dynamic_channel_traffic_log` MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_dynamic_channel_traffic_log'
|
||||
AND COLUMN_NAME = 'member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_dynamic_channel_traffic_log` ADD COLUMN `member_user_id` BIGINT NULL COMMENT ''会员ID'' AFTER `user_id`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_dynamic_channel_traffic_log'
|
||||
AND INDEX_NAME = 'idx_xxk_dynamic_channel_traffic_log_member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_dynamic_channel_traffic_log` ADD INDEX `idx_xxk_dynamic_channel_traffic_log_member_user_id` (`member_user_id`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `xxk_dynamic_proxy_generate_log` MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_dynamic_proxy_generate_log'
|
||||
AND COLUMN_NAME = 'member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_dynamic_proxy_generate_log` ADD COLUMN `member_user_id` BIGINT NULL COMMENT ''会员ID'' AFTER `user_id`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_dynamic_proxy_generate_log'
|
||||
AND INDEX_NAME = 'idx_xxk_dynamic_proxy_generate_log_member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_dynamic_proxy_generate_log` ADD INDEX `idx_xxk_dynamic_proxy_generate_log_member_user_id` (`member_user_id`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `xxk_static_proxy_asset` MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
ALTER TABLE `xxk_static_proxy_whitelist` MODIFY COLUMN `user_id` BIGINT NULL COMMENT '用户ID';
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_static_proxy_whitelist'
|
||||
AND COLUMN_NAME = 'member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_static_proxy_whitelist` ADD COLUMN `member_user_id` BIGINT NULL COMMENT ''会员ID'' AFTER `user_id`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_static_proxy_whitelist'
|
||||
AND INDEX_NAME = 'uk_member_whitelist_ip'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_static_proxy_whitelist` ADD UNIQUE KEY `uk_member_whitelist_ip` (`member_user_id`, `whitelist_ip`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_static_proxy_whitelist'
|
||||
AND INDEX_NAME = 'idx_xxk_static_proxy_whitelist_member_user_id'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_static_proxy_whitelist` ADD INDEX `idx_xxk_static_proxy_whitelist_member_user_id` (`member_user_id`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product'
|
||||
AND COLUMN_NAME = 'upstream_params'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_product` ADD COLUMN `upstream_params` text DEFAULT NULL COMMENT ''上游扩展参数JSON'' AFTER `purpose_web`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product'
|
||||
AND COLUMN_NAME = 'upstream_capabilities'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_product` ADD COLUMN `upstream_capabilities` text DEFAULT NULL COMMENT ''上游能力JSON'' AFTER `upstream_params`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product_duration_multiplier'
|
||||
AND COLUMN_NAME = 'duration_unit'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_product_duration_multiplier` ADD COLUMN `duration_unit` varchar(16) NOT NULL DEFAULT ''DAY'' COMMENT ''时长单位(DAY天/HOUR小时)'' AFTER `duration_days`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product_duration_multiplier'
|
||||
AND COLUMN_NAME = 'duration_value'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_product_duration_multiplier` ADD COLUMN `duration_value` int NOT NULL DEFAULT 1 COMMENT ''时长数值'' AFTER `duration_unit`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
UPDATE `xxk_product_duration_multiplier`
|
||||
SET `duration_unit` = 'DAY',
|
||||
`duration_value` = `duration_days`
|
||||
WHERE (`duration_unit` IS NULL OR `duration_unit` = '' OR `duration_unit` = 'DAY')
|
||||
AND (`duration_value` IS NULL OR `duration_value` <= 1);
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product_duration_multiplier'
|
||||
AND INDEX_NAME = 'uk_product_duration'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists > 0,
|
||||
'ALTER TABLE `xxk_product_duration_multiplier` DROP INDEX `uk_product_duration`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product_duration_multiplier'
|
||||
AND INDEX_NAME = 'uk_product_duration_unit_value'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_product_duration_multiplier` ADD UNIQUE KEY `uk_product_duration_unit_value` (`product_id`, `duration_unit`, `duration_value`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product_duration_multiplier'
|
||||
AND INDEX_NAME = 'idx_product_duration_days'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_product_duration_multiplier` ADD INDEX `idx_product_duration_days` (`product_id`, `duration_days`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'xxk_product_static_country_price' AND COLUMN_NAME = 'price_type');
|
||||
SET @sql = IF(@column_exists = 0, 'ALTER TABLE `xxk_product_static_country_price` ADD COLUMN `price_type` varchar(20) DEFAULT NULL COMMENT ''价格类型(DEFAULT默认/NODE特殊节点)'' AFTER `product_id`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'xxk_product_static_country_price' AND COLUMN_NAME = 'qiyun_product_type');
|
||||
SET @sql = IF(@column_exists = 0, 'ALTER TABLE `xxk_product_static_country_price` ADD COLUMN `qiyun_product_type` varchar(32) DEFAULT NULL COMMENT ''齐云产品类型'' AFTER `price_type`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'xxk_product_static_country_price' AND COLUMN_NAME = 'qiyun_pid');
|
||||
SET @sql = IF(@column_exists = 0, 'ALTER TABLE `xxk_product_static_country_price` ADD COLUMN `qiyun_pid` varchar(64) DEFAULT NULL COMMENT ''齐云项目ID'' AFTER `qiyun_product_type`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'xxk_product_static_country_price' AND COLUMN_NAME = 'qiyun_project_name');
|
||||
SET @sql = IF(@column_exists = 0, 'ALTER TABLE `xxk_product_static_country_price` ADD COLUMN `qiyun_project_name` varchar(255) DEFAULT NULL COMMENT ''齐云项目名称'' AFTER `qiyun_pid`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'xxk_product_static_country_price' AND COLUMN_NAME = 'qiyun_area_id');
|
||||
SET @sql = IF(@column_exists = 0, 'ALTER TABLE `xxk_product_static_country_price` ADD COLUMN `qiyun_area_id` varchar(64) DEFAULT NULL COMMENT ''齐云省份ID'' AFTER `qiyun_project_name`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'xxk_product_static_country_price' AND COLUMN_NAME = 'qiyun_area_name');
|
||||
SET @sql = IF(@column_exists = 0, 'ALTER TABLE `xxk_product_static_country_price` ADD COLUMN `qiyun_area_name` varchar(255) DEFAULT NULL COMMENT ''齐云省份名称'' AFTER `qiyun_area_id`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'xxk_product_static_country_price' AND COLUMN_NAME = 'qiyun_node_id');
|
||||
SET @sql = IF(@column_exists = 0, 'ALTER TABLE `xxk_product_static_country_price` ADD COLUMN `qiyun_node_id` varchar(100) DEFAULT NULL COMMENT ''齐云节点ID'' AFTER `qiyun_area_name`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (SELECT COUNT(1) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = 'xxk_product_static_country_price' AND COLUMN_NAME = 'qiyun_node_name');
|
||||
SET @sql = IF(@column_exists = 0, 'ALTER TABLE `xxk_product_static_country_price` ADD COLUMN `qiyun_node_name` varchar(255) DEFAULT NULL COMMENT ''齐云节点名称'' AFTER `qiyun_node_id`', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `xxk_product` MODIFY COLUMN `currency` varchar(10) DEFAULT 'RMB' COMMENT '币种';
|
||||
ALTER TABLE `xxk_product_static_country_price` MODIFY COLUMN `currency` varchar(10) DEFAULT 'RMB' COMMENT '币种';
|
||||
ALTER TABLE `xxk_proxy_order` MODIFY COLUMN `currency` varchar(10) DEFAULT 'RMB' COMMENT '币种';
|
||||
|
||||
UPDATE `xxk_product` SET `currency` = 'RMB' WHERE `currency` IS NULL OR `currency` = '' OR `currency` = 'USD';
|
||||
UPDATE `xxk_product_static_country_price` SET `currency` = 'RMB' WHERE `currency` IS NULL OR `currency` = '' OR `currency` = 'USD';
|
||||
UPDATE `xxk_proxy_order` SET `currency` = 'RMB' WHERE `currency` IS NULL OR `currency` = '' OR `currency` = 'USD';
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product_static_country_price'
|
||||
AND INDEX_NAME = 'uk_product_country'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists > 0,
|
||||
'ALTER TABLE `xxk_product_static_country_price` DROP INDEX `uk_product_country`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product_static_country_price'
|
||||
AND INDEX_NAME = 'idx_product_country'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_product_static_country_price` ADD INDEX `idx_product_country` (`product_id`, `country_code`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_product_static_country_price'
|
||||
AND INDEX_NAME = 'idx_static_price_qiyun_node'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@index_exists = 0,
|
||||
'ALTER TABLE `xxk_product_static_country_price` ADD INDEX `idx_static_price_qiyun_node` (`product_id`, `price_type`, `qiyun_node_id`) USING BTREE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
UPDATE `xxk_upstream_provider`
|
||||
SET `provider_code` = 'QIYUN01',
|
||||
`provider_name` = '齐云IP',
|
||||
`provider_type` = 'QIYUN',
|
||||
`base_url` = 'https://www.qiyunip.com',
|
||||
`auth_user_id` = NULL,
|
||||
`auth_token` = NULL,
|
||||
`success_codes` = '1'
|
||||
WHERE `provider_type` = 'IPNUX';
|
||||
|
||||
-- 开放 API 模块补丁:应用、申请、独立账户、充值、回调日志和后台菜单。
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_app` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`app_id` VARCHAR(64) NOT NULL COMMENT '应用ID',
|
||||
`app_name` VARCHAR(128) NOT NULL COMMENT '应用名称',
|
||||
`app_secret` VARCHAR(128) NOT NULL COMMENT '应用密钥',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '绑定会员ID',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(0停用 1启用)',
|
||||
`allow_ip_list` TEXT DEFAULT NULL COMMENT '允许访问IP列表,逗号/换行分隔',
|
||||
`callback_url` VARCHAR(255) DEFAULT NULL COMMENT '订单结果回调地址',
|
||||
`callback_secret` VARCHAR(128) DEFAULT NULL COMMENT '订单结果回调签名密钥',
|
||||
`last_auth_time` DATETIME DEFAULT NULL COMMENT '最近换取token时间',
|
||||
`last_auth_ip` VARCHAR(64) DEFAULT NULL COMMENT '最近换取token IP',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_app_app_id` (`app_id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_app_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_open_api_app_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_open_api_app_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放接口应用表';
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_open_api_app'
|
||||
AND COLUMN_NAME = 'callback_url'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_open_api_app` ADD COLUMN `callback_url` VARCHAR(255) DEFAULT NULL COMMENT ''订单结果回调地址'' AFTER `allow_ip_list`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @column_exists = (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @schema_name
|
||||
AND TABLE_NAME = 'xxk_open_api_app'
|
||||
AND COLUMN_NAME = 'callback_secret'
|
||||
);
|
||||
SET @sql = IF(
|
||||
@column_exists = 0,
|
||||
'ALTER TABLE `xxk_open_api_app` ADD COLUMN `callback_secret` VARCHAR(128) DEFAULT NULL COMMENT ''订单结果回调签名密钥'' AFTER `callback_url`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_apply` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`apply_no` VARCHAR(64) NOT NULL COMMENT '申请单号',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`username_snapshot` VARCHAR(64) DEFAULT NULL COMMENT '用户名快照',
|
||||
`mobile_snapshot` VARCHAR(32) DEFAULT NULL COMMENT '手机号快照',
|
||||
`contact_name` VARCHAR(64) NOT NULL COMMENT '联系人',
|
||||
`contact_mobile` VARCHAR(32) DEFAULT NULL COMMENT '联系电话',
|
||||
`contact_email` VARCHAR(128) DEFAULT NULL COMMENT '联系邮箱',
|
||||
`company_name` VARCHAR(128) DEFAULT NULL COMMENT '公司名称',
|
||||
`purpose` VARCHAR(255) NOT NULL COMMENT '申请用途',
|
||||
`scenario_description` TEXT NOT NULL COMMENT '使用场景说明',
|
||||
`allow_ip_list` TEXT DEFAULT NULL COMMENT 'IP白名单',
|
||||
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态(0待审核 1已通过 2已驳回)',
|
||||
`submit_remark` VARCHAR(255) DEFAULT NULL COMMENT '补充说明',
|
||||
`submit_time` DATETIME DEFAULT NULL COMMENT '提交时间',
|
||||
`audit_time` DATETIME DEFAULT NULL COMMENT '审核时间',
|
||||
`audit_by` BIGINT DEFAULT NULL COMMENT '审核人ID',
|
||||
`audit_remark` VARCHAR(255) DEFAULT NULL COMMENT '审核备注',
|
||||
`open_api_app_id` BIGINT DEFAULT NULL COMMENT '关联开放应用ID',
|
||||
`create_by` BIGINT DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_by` BIGINT DEFAULT NULL COMMENT '更新人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_apply_no` (`apply_no`),
|
||||
KEY `idx_xxk_open_api_apply_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_open_api_apply_status` (`status`),
|
||||
KEY `idx_xxk_open_api_apply_submit_time` (`submit_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放接口申请表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_account` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`open_api_app_id` BIGINT NOT NULL COMMENT '开放应用ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`balance` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '余额',
|
||||
`frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '冻结余额',
|
||||
`total_recharge_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '累计充值金额',
|
||||
`total_consume_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '累计消费金额',
|
||||
`total_refund_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '累计退款金额',
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态(0停用 1正常)',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_account_app_id` (`open_api_app_id`),
|
||||
KEY `idx_xxk_open_api_account_member_user_id` (`member_user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API独立账户表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_account_flow` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`flow_no` VARCHAR(64) NOT NULL COMMENT '流水号',
|
||||
`open_api_app_id` BIGINT NOT NULL COMMENT '开放应用ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`biz_type` VARCHAR(64) NOT NULL COMMENT '业务类型',
|
||||
`change_type` VARCHAR(16) NOT NULL COMMENT '变动类型(IN/OUT)',
|
||||
`change_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '变动金额',
|
||||
`before_balance` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '变动前余额',
|
||||
`after_balance` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '变动后余额',
|
||||
`related_order_no` VARCHAR(64) DEFAULT NULL COMMENT '关联单号',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`operate_by` BIGINT DEFAULT NULL COMMENT '操作人ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_account_flow_no` (`flow_no`),
|
||||
KEY `idx_xxk_open_api_account_flow_app_id` (`open_api_app_id`),
|
||||
KEY `idx_xxk_open_api_account_flow_related_order_no` (`related_order_no`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API独立账户流水表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_recharge_order` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`recharge_no` VARCHAR(64) NOT NULL COMMENT '充值单号',
|
||||
`pay_order_no` VARCHAR(64) NOT NULL COMMENT '支付单号',
|
||||
`open_api_app_id` BIGINT NOT NULL COMMENT '开放应用ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`payment_type` VARCHAR(32) NOT NULL COMMENT '支付方式',
|
||||
`pay_status` VARCHAR(32) NOT NULL COMMENT '支付状态',
|
||||
`channel_order_no` VARCHAR(64) DEFAULT NULL COMMENT '渠道订单号',
|
||||
`channel_response` LONGTEXT DEFAULT NULL COMMENT '渠道响应',
|
||||
`client_type` VARCHAR(32) DEFAULT NULL COMMENT '客户端类型',
|
||||
`return_url` VARCHAR(500) DEFAULT NULL COMMENT '回跳地址',
|
||||
`amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '充值金额',
|
||||
`gift_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '赠送金额',
|
||||
`credited_amount` DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '到账金额',
|
||||
`promotion_rule_snapshot` TEXT DEFAULT NULL COMMENT '活动规则快照',
|
||||
`currency` VARCHAR(16) DEFAULT 'USD' COMMENT '币种',
|
||||
`paid_time` DATETIME DEFAULT NULL COMMENT '支付完成时间',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_open_api_recharge_no` (`recharge_no`),
|
||||
UNIQUE KEY `uk_xxk_open_api_pay_order_no` (`pay_order_no`),
|
||||
KEY `idx_xxk_open_api_recharge_order_app_id` (`open_api_app_id`),
|
||||
KEY `idx_xxk_open_api_recharge_order_member_user_id` (`member_user_id`),
|
||||
KEY `idx_xxk_open_api_recharge_order_pay_status` (`pay_status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API独立账户充值单表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_open_api_callback_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`open_api_app_id` BIGINT NOT NULL COMMENT '开放应用ID',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`order_id` BIGINT NOT NULL COMMENT '订单ID',
|
||||
`order_no` VARCHAR(64) NOT NULL COMMENT '订单号',
|
||||
`callback_url` VARCHAR(255) DEFAULT NULL COMMENT '回调地址',
|
||||
`event_type` VARCHAR(64) NOT NULL COMMENT '事件类型',
|
||||
`request_body` LONGTEXT DEFAULT NULL COMMENT '请求报文',
|
||||
`response_body` LONGTEXT DEFAULT NULL COMMENT '响应报文',
|
||||
`response_status` INT DEFAULT NULL COMMENT '响应HTTP状态码',
|
||||
`delivery_status` VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '投递状态(PENDING/SUCCESS/FAIL/SKIPPED)',
|
||||
`error_message` VARCHAR(500) DEFAULT NULL COMMENT '错误信息',
|
||||
`attempt_no` INT NOT NULL DEFAULT 1 COMMENT '尝试次数',
|
||||
`trigger_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '触发时间',
|
||||
`finish_time` DATETIME DEFAULT NULL COMMENT '完成时间',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_xxk_open_api_callback_log_app_id` (`open_api_app_id`),
|
||||
KEY `idx_xxk_open_api_callback_log_order_no` (`order_no`),
|
||||
KEY `idx_xxk_open_api_callback_log_status` (`delivery_status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放API订单回调日志表';
|
||||
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1220, 1100, '0,1100', '开放API管理', 'C', NULL, '/open-api-manage', 'Layout', NULL,
|
||||
1, 0, 1, 13, 'api', '/open-api-manage/open-api', NOW(), NOW(), NULL),
|
||||
(1221, 1220, '0,1100,1220', '开放API申请审核', 'M', 'ProxyOpenApiApply', 'open-api', 'proxy/open-api/index', NULL,
|
||||
0, 1, 1, 1, 'form', NULL, NOW(), NOW(), NULL),
|
||||
(122101, 1221, '0,1100,1220,1221', '开放API申请查询', 'B', NULL, '', NULL, 'openapi:apply:list',
|
||||
NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL),
|
||||
(122102, 1221, '0,1100,1220,1221', '开放API申请审核', 'B', NULL, '', NULL, 'openapi:apply:audit',
|
||||
NULL, NULL, 1, 2, '', NULL, NOW(), NOW(), NULL),
|
||||
(1222, 1220, '0,1100,1220', '开放API回调日志', 'M', 'ProxyOpenApiCallback', 'open-api-callback', 'proxy/open-api-callback/index', NULL,
|
||||
0, 1, 1, 2, 'notification', NULL, NOW(), NOW(), NULL),
|
||||
(122201, 1222, '0,1100,1220,1222', '开放API回调日志查询', 'B', NULL, '', NULL, 'openapi:callback-log:list',
|
||||
NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL),
|
||||
(122202, 1222, '0,1100,1220,1222', '开放API回调日志重发', 'B', NULL, '', NULL, 'openapi:callback-log:retry',
|
||||
NULL, NULL, 1, 2, '', NULL, NOW(), NOW(), NULL),
|
||||
(1223, 1220, '0,1100,1220', '开放API账户中心', 'M', 'ProxyOpenApiAccount', 'open-api-account', 'proxy/open-api-account/index', NULL,
|
||||
0, 1, 0, 3, 'wallet', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`update_time` = NOW();
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 1220), (2, 1221), (2, 122101), (2, 122102), (2, 1222), (2, 122201), (2, 122202), (2, 1223);
|
||||
@@ -0,0 +1,26 @@
|
||||
ALTER TABLE `xxk_proxy_city_library`
|
||||
RENAME TO `xxk_proxy_region`;
|
||||
|
||||
ALTER TABLE `xxk_proxy_region`
|
||||
CHANGE COLUMN `city_code` `region_code` varchar(64) NOT NULL COMMENT '地区编号',
|
||||
CHANGE COLUMN `city_name_zh` `region_name_zh` varchar(100) NOT NULL COMMENT '地区中文名',
|
||||
CHANGE COLUMN `city_name_en` `region_name` varchar(100) NOT NULL COMMENT '地区名称',
|
||||
CHANGE COLUMN `icon_url` `icon_url` varchar(500) NOT NULL COMMENT '地区图标',
|
||||
DROP COLUMN `country_code`,
|
||||
DROP COLUMN `country_name`;
|
||||
|
||||
ALTER TABLE `xxk_product_static_country_price`
|
||||
ADD COLUMN `region_id` bigint DEFAULT NULL COMMENT '地区ID' AFTER `product_id`,
|
||||
ADD COLUMN `region_code` varchar(64) DEFAULT NULL COMMENT '地区编号' AFTER `region_id`,
|
||||
ADD COLUMN `region_name` varchar(100) DEFAULT NULL COMMENT '地区名称' AFTER `region_code`,
|
||||
ADD COLUMN `region_name_zh` varchar(100) DEFAULT NULL COMMENT '地区中文名' AFTER `region_name`;
|
||||
|
||||
UPDATE `xxk_product_static_country_price`
|
||||
SET
|
||||
`region_code` = `country_code`,
|
||||
`region_name` = IFNULL(`region_name`, `country_name`),
|
||||
`region_name_zh` = IFNULL(`region_name_zh`, `country_name`)
|
||||
WHERE `region_code` IS NULL OR `region_name_zh` IS NULL;
|
||||
|
||||
ALTER TABLE `xxk_product_static_country_price`
|
||||
ADD KEY `idx_static_price_region_id` (`region_id`);
|
||||
@@ -0,0 +1,38 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 代理平台主管演示角色
|
||||
SET @proxy_role_code = 'PROXY_MANAGER';
|
||||
|
||||
INSERT INTO `sys_role`
|
||||
(`name`, `code`, `sort`, `status`, `data_scope`, `create_by`, `create_time`, `update_by`, `update_time`, `is_deleted`)
|
||||
SELECT '代理平台主管', @proxy_role_code, 80, 1, 1, 1, now(), 1, now(), 0
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `sys_role`
|
||||
WHERE `code` = @proxy_role_code
|
||||
);
|
||||
|
||||
SET @proxy_role_id = (
|
||||
SELECT `id`
|
||||
FROM `sys_role`
|
||||
WHERE `code` = @proxy_role_code
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`)
|
||||
SELECT @proxy_role_id, `id`
|
||||
FROM `sys_menu`
|
||||
WHERE `id` IN (
|
||||
1100,
|
||||
1110, 11101, 11102, 11103, 11104,
|
||||
1120, 11201, 11202, 11203, 11204,
|
||||
1130, 11301, 11302, 11303, 11304,
|
||||
1140, 11401, 11402, 11403, 11404,
|
||||
1150, 11501, 11502, 11503,
|
||||
1160, 11601, 11602, 11603,
|
||||
1170, 11701, 11702,
|
||||
1180, 11801, 11802, 11803, 11804,
|
||||
1190, 11901, 11902,
|
||||
1200, 12001, 12002, 12003
|
||||
);
|
||||
@@ -0,0 +1,29 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 上游请求日志菜单升级脚本
|
||||
-- 说明:
|
||||
-- 1. 在代理平台下新增“上游请求日志”菜单。
|
||||
-- 2. 用于查看上游代理平台请求与响应日志,辅助联调和失败排查。
|
||||
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(1185, 1100, '0,1100', '上游请求日志', 'M', 'ProxyUpstreamRequestLog', 'upstream-request-log', 'proxy/upstream-request-log/index', NULL,
|
||||
0, 1, 1, 11, 'histogram', NULL, NOW(), NOW(), NULL),
|
||||
(11851, 1185, '0,1100,1185', '上游请求日志查询', 'B', NULL, '', NULL, 'proxy:upstream-request-log:list',
|
||||
NULL, NULL, 1, 1, '', NULL, NOW(), NOW(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`icon` = VALUES(`icon`),
|
||||
`update_time` = NOW();
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES
|
||||
(2, 1185), (2, 11851);
|
||||
@@ -0,0 +1,9 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 钱包流水表 update_time 字段补齐脚本
|
||||
-- 说明:
|
||||
-- 1. 当前代码中的钱包流水实体继承了 BaseEntity,插入时会自动写入 update_time。
|
||||
-- 2. 部分旧库的 xxk_wallet_flow 表缺少 update_time,导致支付、退款等钱包流水写入时报错。
|
||||
|
||||
ALTER TABLE `xxk_wallet_flow`
|
||||
ADD COLUMN `update_time` DATETIME NULL COMMENT '更新时间' AFTER `create_time`;
|
||||
@@ -0,0 +1,22 @@
|
||||
USE youlai_admin;
|
||||
|
||||
-- 钱包充值订单管理菜单
|
||||
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(11910, 1190, '0,1100,1190', '充值订单', 'C', 'ProxyWalletRecharge', 'wallet-recharge', 'proxy/wallet-recharge/index', NULL,
|
||||
0, 1, 1, 3, 'wallet', NULL, now(), now(), NULL),
|
||||
(11911, 11910, '0,1100,1190,11910', '充值订单查询', 'B', NULL, '', NULL, 'proxy:wallet:recharge-order:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
@@ -0,0 +1,50 @@
|
||||
-- 钱包在线充值单升级脚本
|
||||
-- 说明:
|
||||
-- 1. 新增会员钱包在线充值单,独立承接 DaxPay 支付链路。
|
||||
-- 2. 钱包入账仍然落到 xxk_wallet_account / xxk_wallet_flow。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `xxk_wallet_recharge_order` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`recharge_no` VARCHAR(64) NOT NULL COMMENT '充值单号',
|
||||
`pay_order_no` VARCHAR(64) NOT NULL COMMENT '支付单号',
|
||||
`member_user_id` BIGINT NOT NULL COMMENT '会员ID',
|
||||
`payment_type` VARCHAR(32) NOT NULL COMMENT '支付方式(ALIPAY/WECHAT)',
|
||||
`pay_status` VARCHAR(32) NOT NULL DEFAULT 'INIT' COMMENT '支付状态(INIT/PAYING/PAID/FAIL/CLOSED)',
|
||||
`channel_order_no` VARCHAR(128) DEFAULT NULL COMMENT '渠道支付单号',
|
||||
`channel_response` TEXT DEFAULT NULL COMMENT '渠道原始响应',
|
||||
`client_type` VARCHAR(32) DEFAULT NULL COMMENT '客户端类型(PC/H5/APP/MINI_PROGRAM)',
|
||||
`return_url` VARCHAR(500) DEFAULT NULL COMMENT '支付完成跳转地址',
|
||||
`amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '充值金额',
|
||||
`currency` VARCHAR(16) DEFAULT 'CNY' COMMENT '币种',
|
||||
`paid_time` DATETIME DEFAULT NULL COMMENT '支付成功时间',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除(0否 1是)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_xxk_wallet_recharge_no` (`recharge_no`),
|
||||
UNIQUE KEY `uk_xxk_wallet_recharge_pay_order_no` (`pay_order_no`),
|
||||
KEY `idx_xxk_wallet_recharge_member_status` (`member_user_id`, `pay_status`, `create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='钱包在线充值单';
|
||||
|
||||
|
||||
-- 钱包充值订单管理菜单
|
||||
|
||||
INSERT INTO `sys_menu`
|
||||
(`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`,
|
||||
`always_show`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`)
|
||||
VALUES
|
||||
(11910, 1190, '0,1100,1190', '充值订单', 'C', 'ProxyWalletRecharge', 'wallet-recharge', 'proxy/wallet-recharge/index', NULL,
|
||||
0, 1, 1, 3, 'wallet', NULL, now(), now(), NULL),
|
||||
(11911, 11910, '0,1100,1190,11910', '充值订单查询', 'B', NULL, '', NULL, 'proxy:wallet:recharge-order:list',
|
||||
NULL, NULL, 1, 1, '', NULL, now(), now(), NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`name` = VALUES(`name`),
|
||||
`route_name` = VALUES(`route_name`),
|
||||
`route_path` = VALUES(`route_path`),
|
||||
`component` = VALUES(`component`),
|
||||
`perm` = VALUES(`perm`),
|
||||
`keep_alive` = VALUES(`keep_alive`),
|
||||
`visible` = VALUES(`visible`),
|
||||
`sort` = VALUES(`sort`),
|
||||
`update_time` = now();
|
||||
@@ -0,0 +1,586 @@
|
||||
|
||||
# YouLai_Admin 数据库(MySQL 5.7 ~ MySQL 8.x)
|
||||
# Copyright (c) 2021-present, youlai.tech
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 1. 创建数据库
|
||||
-- ----------------------------
|
||||
CREATE DATABASE IF NOT EXISTS youlai_admin CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 2. 创建表 && 数据初始化
|
||||
-- ----------------------------
|
||||
USE youlai_admin;
|
||||
|
||||
SET NAMES utf8mb4; # 设置字符集
|
||||
SET FOREIGN_KEY_CHECKS = 0; # 关闭外键检查,加快导入速度
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dept
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dept`;
|
||||
CREATE TABLE `sys_dept` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`name` varchar(100) NOT NULL COMMENT '部门名称',
|
||||
`code` varchar(100) NOT NULL COMMENT '部门编号',
|
||||
`parent_id` bigint DEFAULT 0 COMMENT '父节点id',
|
||||
`tree_path` varchar(255) NOT NULL COMMENT '父节点id路径',
|
||||
`sort` smallint DEFAULT 0 COMMENT '显示顺序',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`create_by` bigint NULL COMMENT '创建人ID',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_by` bigint NULL COMMENT '修改人ID',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除标识(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_code`(`code` ASC) USING BTREE COMMENT '部门编号唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '部门管理表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_dept
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dept` VALUES (1, '有来技术', 'YOULAI', 0, '0', 1, 1, 1, NULL, 1, now(), 0);
|
||||
INSERT INTO `sys_dept` VALUES (2, '研发部门', 'RD001', 1, '0,1', 1, 1, 2, NULL, 2, now(), 0);
|
||||
INSERT INTO `sys_dept` VALUES (3, '测试部门', 'QA001', 1, '0,1', 1, 1, 2, NULL, 2, now(), 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dict
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dict`;
|
||||
CREATE TABLE `sys_dict` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键 ',
|
||||
`dict_code` varchar(50) COMMENT '类型编码',
|
||||
`name` varchar(50) COMMENT '类型名称',
|
||||
`status` tinyint(1) DEFAULT '0' COMMENT '状态(0:正常;1:禁用)',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
`is_deleted` tinyint DEFAULT '0' COMMENT '是否删除(1-删除,0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_dict_code` (`dict_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典类型表';
|
||||
-- ----------------------------
|
||||
-- Records of sys_dict
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dict` VALUES (1, 'gender', '性别', 1, NULL, now() , 1,now(), 1,0);
|
||||
INSERT INTO `sys_dict` VALUES (2, 'notice_type', '通知类型', 1, NULL, now(), 1,now(), 1,0);
|
||||
INSERT INTO `sys_dict` VALUES (3, 'notice_level', '通知级别', 1, NULL, now(), 1,now(), 1,0);
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dict_item
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dict_item`;
|
||||
CREATE TABLE `sys_dict_item` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`dict_code` varchar(50) COMMENT '关联字典编码,与sys_dict表中的dict_code对应',
|
||||
`value` varchar(50) COMMENT '字典项值',
|
||||
`label` varchar(100) COMMENT '字典项标签',
|
||||
`tag_type` varchar(50) COMMENT '标签类型,用于前端样式展示(如success、warning等)',
|
||||
`status` tinyint DEFAULT '0' COMMENT '状态(1-正常,0-禁用)',
|
||||
`sort` int DEFAULT '0' COMMENT '排序',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典项表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_dict_item
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dict_item` VALUES (1, 'gender', '1', '男', 'primary', 1, 1, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (2, 'gender', '2', '女', 'danger', 1, 2, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (3, 'gender', '0', '保密', 'info', 1, 3, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (4, 'notice_type', '1', '系统升级', 'success', 1, 1, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (5, 'notice_type', '2', '系统维护', 'primary', 1, 2, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (6, 'notice_type', '3', '安全警告', 'danger', 1, 3, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (7, 'notice_type', '4', '假期通知', 'success', 1, 4, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (8, 'notice_type', '5', '公司新闻', 'primary', 1, 5, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (9, 'notice_type', '99', '其他', 'info', 1, 99, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (10, 'notice_level', 'L', '低', 'info', 1, 1, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (11, 'notice_level', 'M', '中', 'warning', 1, 2, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (12, 'notice_level', 'H', '高', 'danger', 1, 3, '', now(), 1,now(),1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_menu
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_menu`;
|
||||
CREATE TABLE `sys_menu` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`parent_id` bigint NOT NULL COMMENT '父菜单ID',
|
||||
`tree_path` varchar(255) COMMENT '父节点ID路径',
|
||||
`name` varchar(64) NOT NULL COMMENT '菜单名称',
|
||||
`type` char(1) NOT NULL COMMENT '菜单类型(C-目录 M-菜单 B-按钮)',
|
||||
`route_name` varchar(255) COMMENT '路由名称(Vue Router 中用于命名路由)',
|
||||
`route_path` varchar(128) COMMENT '路由路径(Vue Router 中定义的 URL 路径)',
|
||||
`component` varchar(128) COMMENT '组件路径(组件页面完整路径,相对于 src/views/,缺省后缀 .vue)',
|
||||
`perm` varchar(128) COMMENT '【按钮】权限标识',
|
||||
`always_show` tinyint DEFAULT 0 COMMENT '【目录】只有一个子路由是否始终显示(1-是 0-否)',
|
||||
`keep_alive` tinyint DEFAULT 0 COMMENT '【菜单】是否开启页面缓存(1-是 0-否)',
|
||||
`visible` tinyint(1) DEFAULT 1 COMMENT '显示状态(1-显示 0-隐藏)',
|
||||
`sort` int DEFAULT 0 COMMENT '排序',
|
||||
`icon` varchar(64) COMMENT '菜单图标',
|
||||
`redirect` varchar(128) COMMENT '跳转路径',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`params` json NULL COMMENT '路由参数',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统菜单表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_menu
|
||||
-- ----------------------------
|
||||
-- 顶级目录(1-9):系统/代码生成/文档/接口文档/组件/演示/多级/路由
|
||||
INSERT INTO `sys_menu` VALUES (1, 0, '0', '系统管理', 'C', '', '/system', 'Layout', NULL, NULL, NULL, 1, 1, 'system', '/system/user', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2, 0, '0', '代码生成', 'C', '', '/codegen', 'Layout', NULL, NULL, NULL, 1, 2, 'code', '/codegen/index', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (4, 0, '0', '平台文档', 'C', '', '/doc', 'Layout', NULL, NULL, NULL, 1, 4, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (5, 0, '0', '接口文档', 'C', '', '/api', 'Layout', NULL, NULL, NULL, 1, 5, 'api', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (6, 0, '0', '组件封装', 'C', '', '/component', 'Layout', NULL, NULL, NULL, 1, 6, 'menu', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (7, 0, '0', '功能演示', 'C', '', '/function', 'Layout', NULL, NULL, NULL, 1, 7, 'menu', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (8, 0, '0', '多级菜单', 'C', NULL, '/multi-level', 'Layout', NULL, 1, NULL, 1, 8, 'cascader', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (9, 0, '0', '路由参数', 'C', '', '/route-param', 'Layout', NULL, NULL, NULL, 1, 9, 'el-icon-ElementPlus', '', now(), now(), NULL);
|
||||
|
||||
-- 系统管理
|
||||
INSERT INTO `sys_menu` VALUES (210, 1, '0,1', '用户管理', 'M', 'User', 'user', 'system/user/index', NULL, NULL, 1, 1, 1, 'el-icon-User', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2101, 210, '0,1,210', '用户查询', 'B', NULL, '', NULL, 'sys:user:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2102, 210, '0,1,210', '用户新增', 'B', NULL, '', NULL, 'sys:user:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2103, 210, '0,1,210', '用户编辑', 'B', NULL, '', NULL, 'sys:user:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2104, 210, '0,1,210', '用户删除', 'B', NULL, '', NULL, 'sys:user:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2105, 210, '0,1,210', '重置密码', 'B', NULL, '', NULL, 'sys:user:reset-password', NULL, NULL, 1, 5, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2106, 210, '0,1,210', '用户导入', 'B', NULL, '', NULL, 'sys:user:import', NULL, NULL, 1, 6, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2107, 210, '0,1,210', '用户导出', 'B', NULL, '', NULL, 'sys:user:export', NULL, NULL, 1, 7, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (220, 1, '0,1', '角色管理', 'M', 'Role', 'role', 'system/role/index', NULL, NULL, 1, 1, 2, 'role', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2201, 220, '0,1,220', '角色查询', 'B', NULL, '', NULL, 'sys:role:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2202, 220, '0,1,220', '角色新增', 'B', NULL, '', NULL, 'sys:role:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2203, 220, '0,1,220', '角色编辑', 'B', NULL, '', NULL, 'sys:role:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2204, 220, '0,1,220', '角色删除', 'B', NULL, '', NULL, 'sys:role:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2205, 220, '0,1,220', '角色分配权限', 'B', NULL, '', NULL, 'sys:role:assign', NULL, NULL, 1, 5, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (230, 1, '0,1', '菜单管理', 'M', 'SysMenu', 'menu', 'system/menu/index', NULL, NULL, 1, 1, 3, 'menu', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2301, 230, '0,1,230', '菜单查询', 'B', NULL, '', NULL, 'sys:menu:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2302, 230, '0,1,230', '菜单新增', 'B', NULL, '', NULL, 'sys:menu:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2303, 230, '0,1,230', '菜单编辑', 'B', NULL, '', NULL, 'sys:menu:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2304, 230, '0,1,230', '菜单删除', 'B', NULL, '', NULL, 'sys:menu:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (240, 1, '0,1', '部门管理', 'M', 'Dept', 'dept', 'system/dept/index', NULL, NULL, 1, 1, 4, 'tree', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2401, 240, '0,1,240', '部门查询', 'B', NULL, '', NULL, 'sys:dept:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2402, 240, '0,1,240', '部门新增', 'B', NULL, '', NULL, 'sys:dept:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2403, 240, '0,1,240', '部门编辑', 'B', NULL, '', NULL, 'sys:dept:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2404, 240, '0,1,240', '部门删除', 'B', NULL, '', NULL, 'sys:dept:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (250, 1, '0,1', '字典管理', 'M', 'Dict', 'dict', 'system/dict/index', NULL, NULL, 1, 1, 5, 'dict', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2501, 250, '0,1,250', '字典查询', 'B', NULL, '', NULL, 'sys:dict:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2502, 250, '0,1,250', '字典新增', 'B', NULL, '', NULL, 'sys:dict:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2503, 250, '0,1,250', '字典编辑', 'B', NULL, '', NULL, 'sys:dict:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2504, 250, '0,1,250', '字典删除', 'B', NULL, '', NULL, 'sys:dict:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (251, 1, '0,1', '字典项', 'M', 'DictItem', 'dict-item', 'system/dict/dict-item', NULL, 0, 1, 0, 6, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2511, 251, '0,1,251', '字典项查询', 'B', NULL, '', NULL, 'sys:dict-item:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2512, 251, '0,1,251', '字典项新增', 'B', NULL, '', NULL, 'sys:dict-item:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2513, 251, '0,1,251', '字典项编辑', 'B', NULL, '', NULL, 'sys:dict-item:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2514, 251, '0,1,251', '字典项删除', 'B', NULL, '', NULL, 'sys:dict-item:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (260, 1, '0,1', '系统日志', 'M', 'Log', 'log', 'system/log/index', NULL, 0, 1, 1, 7, 'document', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2601, 260, '0,1,260', '日志查询', 'B', NULL, '', NULL, 'sys:log:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (270, 1, '0,1', '系统配置', 'M', 'Config', 'config', 'system/config/index', NULL, 0, 1, 1, 8, 'setting', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2701, 270, '0,1,270', '系统配置查询', 'B', NULL, '', NULL, 'sys:config:list', 0, 1, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2702, 270, '0,1,270', '系统配置新增', 'B', NULL, '', NULL, 'sys:config:create', 0, 1, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2703, 270, '0,1,270', '系统配置修改', 'B', NULL, '', NULL, 'sys:config:update', 0, 1, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2704, 270, '0,1,270', '系统配置删除', 'B', NULL, '', NULL, 'sys:config:delete', 0, 1, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2705, 270, '0,1,270', '系统配置刷新', 'B', NULL, '', NULL, 'sys:config:refresh', 0, 1, 1, 5, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (280, 1, '0,1', '通知公告', 'M', 'Notice', 'notice', 'system/notice/index', NULL, NULL, NULL, 1, 9, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2801, 280, '0,1,280', '通知查询', 'B', NULL, '', NULL, 'sys:notice:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2802, 280, '0,1,280', '通知新增', 'B', NULL, '', NULL, 'sys:notice:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2803, 280, '0,1,280', '通知编辑', 'B', NULL, '', NULL, 'sys:notice:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2804, 280, '0,1,280', '通知删除', 'B', NULL, '', NULL, 'sys:notice:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2805, 280, '0,1,280', '通知发布', 'B', NULL, '', NULL, 'sys:notice:publish', 0, 1, 1, 5, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2806, 280, '0,1,280', '通知撤回', 'B', NULL, '', NULL, 'sys:notice:revoke', 0, 1, 1, 6, '', NULL, now(), now(), NULL);
|
||||
|
||||
-- 代码生成
|
||||
INSERT INTO `sys_menu` VALUES (310, 2, '0,2', '代码生成', 'M', 'Codegen', 'codegen', 'codegen/index', NULL, NULL, 1, 1, 1, 'code', NULL, now(), now(), NULL);
|
||||
|
||||
-- 平台文档(外链通过 route_path 识别)
|
||||
INSERT INTO `sys_menu` VALUES (501, 4, '0,4', '平台文档(外链)', 'M', NULL, 'https://juejin.cn/post/7228990409909108793', '', NULL, NULL, NULL, 1, 1, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (502, 4, '0,4', '后端文档', 'M', NULL, 'https://youlai.blog.csdn.net/article/details/145178880', '', NULL, NULL, NULL, 1, 2, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (503, 4, '0,4', '移动端文档', 'M', NULL, 'https://youlai.blog.csdn.net/article/details/143222890', '', NULL, NULL, NULL, 1, 3, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (504, 4, '0,4', '内部文档', 'M', NULL, 'internal-doc', 'demo/internal-doc', NULL, NULL, NULL, 1, 4, 'document', '', now(), now(), NULL);
|
||||
|
||||
-- 接口文档
|
||||
INSERT INTO `sys_menu` VALUES (601, 5, '0,5', 'Apifox', 'M', 'Apifox', 'apifox', 'demo/api/apifox', NULL, NULL, 1, 1, 1, 'api', '', now(), now(), NULL);
|
||||
|
||||
-- 组件封装
|
||||
INSERT INTO `sys_menu` VALUES (701, 6, '0,6', '富文本编辑器', 'M', 'WangEditor', 'wang-editor', 'demo/wang-editor', NULL, NULL, 1, 1, 2, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (702, 6, '0,6', '图片上传', 'M', 'Upload', 'upload', 'demo/upload', NULL, NULL, 1, 1, 3, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (703, 6, '0,6', '图标选择器', 'M', 'IconSelect', 'icon-select', 'demo/icon-select', NULL, NULL, 1, 1, 4, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (704, 6, '0,6', '字典组件', 'M', 'DictDemo', 'dict-demo', 'demo/dictionary', NULL, NULL, 1, 1, 4, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (705, 6, '0,6', '增删改查', 'M', 'Curd', 'curd', 'demo/curd/index', NULL, NULL, 1, 1, 0, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (706, 6, '0,6', '列表选择器', 'M', 'TableSelect', 'table-select', 'demo/table-select/index', NULL, NULL, 1, 1, 1, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (707, 6, '0,6', '拖拽组件', 'M', 'Drag', 'drag', 'demo/drag', NULL, NULL, NULL, 1, 5, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (708, 6, '0,6', '滚动文本', 'M', 'TextScroll', 'text-scroll', 'demo/text-scroll', NULL, NULL, NULL, 1, 6, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (709, 6, '0,6', '自适应表格操作列', 'M', 'AutoOperationColumn', 'operation-column', 'demo/auto-operation-column', NULL, NULL, 1, 1, 1, '', '', now(), now(), NULL);
|
||||
|
||||
-- 功能演示
|
||||
INSERT INTO `sys_menu` VALUES (801, 7, '0,7', 'Icons', 'M', 'IconDemo', 'icon-demo', 'demo/icons', NULL, NULL, 1, 1, 2, 'el-icon-Notification', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (802, 7, '0,7', '字典实时同步', 'M', 'DictSync', 'dict-sync', 'demo/dict-sync', NULL, NULL, NULL, 1, 3, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (803, 7, '0,7', 'VxeTable', 'M', 'VxeTable', 'vxe-table', 'demo/vxe-table/index', NULL, NULL, 1, 1, 4, 'el-icon-MagicStick', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (804, 7, '0,7', 'CURD单文件', 'M', 'CurdSingle', 'curd-single', 'demo/curd-single', NULL, NULL, 1, 1, 5, 'el-icon-Reading', '', now(), now(), NULL);
|
||||
|
||||
-- 多级菜单示例
|
||||
INSERT INTO `sys_menu` VALUES (910, 8, '0,8', '菜单一级', 'C', NULL, 'multi-level1', 'Layout', NULL, 1, NULL, 1, 1, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (911, 910, '0,8,910', '菜单二级', 'C', NULL, 'multi-level2', 'Layout', NULL, 0, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (912, 911, '0,8,910,911', '菜单三级-1', 'M', NULL, 'multi-level3-1', 'demo/multi-level/children/children/level3-1', NULL, 0, 1, 1, 1, '', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (913, 911, '0,8,910,911', '菜单三级-2', 'M', NULL, 'multi-level3-2', 'demo/multi-level/children/children/level3-2', NULL, 0, 1, 1, 2, '', '', now(), now(), NULL);
|
||||
|
||||
-- 路由参数
|
||||
INSERT INTO `sys_menu` VALUES (1001, 9, '0,9', '参数(type=1)', 'M', 'RouteParamType1', 'route-param-type1', 'demo/route-param', NULL, 0, 1, 1, 1, 'el-icon-Star', NULL, now(), now(), '{\"type\": \"1\"}');
|
||||
INSERT INTO `sys_menu` VALUES (1002, 9, '0,9', '参数(type=2)', 'M', 'RouteParamType2', 'route-param-type2', 'demo/route-param', NULL, 0, 1, 1, 2, 'el-icon-StarFilled', NULL, now(), now(), '{\"type\": \"2\"}');
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role`;
|
||||
CREATE TABLE `sys_role` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(64) NOT NULL COMMENT '角色名称',
|
||||
`code` varchar(32) NOT NULL COMMENT '角色编码',
|
||||
`sort` int NULL COMMENT '显示顺序',
|
||||
`status` tinyint(1) DEFAULT 1 COMMENT '角色状态(1-正常 0-停用)',
|
||||
`data_scope` tinyint NULL COMMENT '数据权限(1-所有数据 2-部门及子部门数据 3-本部门数据 4-本人数据 5-自定义部门数据)',
|
||||
`create_by` bigint NULL COMMENT '创建人 ID',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_by` bigint NULL COMMENT '更新人ID',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint(1) DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_name`(`name` ASC) USING BTREE COMMENT '角色名称唯一索引',
|
||||
UNIQUE INDEX `uk_code`(`code` ASC) USING BTREE COMMENT '角色编码唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统角色表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_role
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_role` VALUES (1, '超级管理员', 'ROOT', 1, 1, 1, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (2, '系统管理员', 'ADMIN', 2, 1, 1, NULL, now(), NULL, NULL, 0);
|
||||
INSERT INTO `sys_role` VALUES (3, '访问游客', 'GUEST', 3, 1, 3, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (4, '部门主管', 'DEPT_MANAGER', 4, 1, 2, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (5, '部门成员', 'DEPT_MEMBER', 5, 1, 3, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (6, '普通员工', 'EMPLOYEE', 6, 1, 4, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (7, '自定义权限用户', 'CUSTOM_USER', 7, 1, 5, NULL, now(), NULL, now(), 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role_menu
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role_menu`;
|
||||
CREATE TABLE `sys_role_menu` (
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
`menu_id` bigint NOT NULL COMMENT '菜单ID',
|
||||
UNIQUE INDEX `uk_roleid_menuid`(`role_id` ASC, `menu_id` ASC) USING BTREE COMMENT '角色菜单唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '角色菜单关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role_dept
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role_dept`;
|
||||
CREATE TABLE `sys_role_dept` (
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
`dept_id` bigint NOT NULL COMMENT '部门ID',
|
||||
UNIQUE INDEX `uk_roleid_deptid`(`role_id` ASC, `dept_id` ASC) USING BTREE COMMENT '角色部门唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '角色部门关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_role_dept
|
||||
-- ----------------------------
|
||||
INSERT IGNORE INTO `sys_role_dept` VALUES (7, 1);
|
||||
INSERT IGNORE INTO `sys_role_dept` VALUES (7, 2);
|
||||
|
||||
-- ============================================
|
||||
-- 系统管理员角色菜单权限(role_id=2)
|
||||
-- 顶级目录
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 1), (2, 2), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9);
|
||||
-- 系统管理
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 210), (2, 2101), (2, 2102), (2, 2103), (2, 2104), (2, 2105), (2, 2106), (2, 2107);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 220), (2, 2201), (2, 2202), (2, 2203), (2, 2204), (2, 2205);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 230), (2, 2301), (2, 2302), (2, 2303), (2, 2304);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 240), (2, 2401), (2, 2402), (2, 2403), (2, 2404);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 250), (2, 2501), (2, 2502), (2, 2503), (2, 2504);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 251), (2, 2511), (2, 2512), (2, 2513), (2, 2514);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 260), (2, 2601);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 270), (2, 2701), (2, 2702), (2, 2703), (2, 2704), (2, 2705);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 280), (2, 2801), (2, 2802), (2, 2803), (2, 2804), (2, 2805), (2, 2806);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 210), (4, 2101), (4, 2102), (4, 2103), (4, 2104), (4, 2105), (4, 2106), (4, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 220), (4, 2201), (4, 2202), (4, 2203), (4, 2204), (4, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 210), (5, 2101), (5, 2102), (5, 2103), (5, 2104), (5, 2105), (5, 2106), (5, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 220), (5, 2201), (5, 2202), (5, 2203), (5, 2204), (5, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 210), (6, 2101), (6, 2102), (6, 2103), (6, 2104), (6, 2105), (6, 2106), (6, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 220), (6, 2201), (6, 2202), (6, 2203), (6, 2204), (6, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 210), (7, 2101), (7, 2102), (7, 2103), (7, 2104), (7, 2105), (7, 2106), (7, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 220), (7, 2201), (7, 2202), (7, 2203), (7, 2204), (7, 2205);
|
||||
-- 代码生成
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 310);
|
||||
-- 平台文档
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 501), (2, 502), (2, 503), (2, 504);
|
||||
-- 接口文档
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 601);
|
||||
-- 组件封装
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 701), (2, 702), (2, 703), (2, 704), (2, 705), (2, 706), (2, 707), (2, 708), (2, 709);
|
||||
-- 功能演示 / 多级菜单
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 801), (2, 802), (2, 803), (2, 804), (2, 910), (2, 911), (2, 912), (2, 913);
|
||||
-- 路由参数
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 1001), (2, 1002);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user`;
|
||||
CREATE TABLE `sys_user` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(64) COMMENT '用户名',
|
||||
`nickname` varchar(64) COMMENT '昵称',
|
||||
`gender` tinyint(1) DEFAULT 1 COMMENT '性别((1-男 2-女 0-保密)',
|
||||
`password` varchar(100) COMMENT '密码',
|
||||
`dept_id` int COMMENT '部门ID',
|
||||
`avatar` varchar(255) COMMENT '用户头像',
|
||||
`mobile` varchar(20) COMMENT '联系方式',
|
||||
`status` tinyint(1) DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`email` varchar(128) COMMENT '用户邮箱',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
`is_deleted` tinyint(1) DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统用户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_user` VALUES (1, 'root', '有来技术', 0, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', NULL, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345677', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (2, 'admin', '系统管理员', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18888888888', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (3, 'test', '测试小用户', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 3, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345679', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (4, 'dept_manager', '部门主管', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345680', 1, 'manager@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (5, 'dept_member', '部门成员', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345681', 1, 'member@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (6, 'employee', '普通员工', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 2, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345682', 1, 'employee@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (7, 'custom_user', '自定义权限用户', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 3, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345683', 1, 'custom@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user_role
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user_role`;
|
||||
CREATE TABLE `sys_user_role` (
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
PRIMARY KEY (`user_id`, `role_id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '用户角色关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_user_role
|
||||
-- ----------------------------
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (1, 1);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (2, 2);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (3, 3);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (4, 4);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (5, 5);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (6, 6);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (7, 7);
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_log`;
|
||||
CREATE TABLE `sys_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`module` TINYINT NOT NULL COMMENT '模块,数字枚举,参考 LogModule 枚举',
|
||||
`action_type` TINYINT NOT NULL COMMENT '操作类型,数字枚举,参考 ActionType 枚举',
|
||||
`title` VARCHAR(100) NOT NULL COMMENT '前端显示标题',
|
||||
`content` TEXT COMMENT '自定义日志内容',
|
||||
`operator_id` BIGINT COMMENT '操作人ID',
|
||||
`operator_name` VARCHAR(50) COMMENT '操作人名称',
|
||||
`request_uri` VARCHAR(255) COMMENT '请求路径',
|
||||
`request_method` VARCHAR(10) COMMENT '请求方法',
|
||||
`ip` VARCHAR(45) COMMENT 'IP地址',
|
||||
`province` VARCHAR(100) COMMENT '省份',
|
||||
`city` VARCHAR(100) COMMENT '城市',
|
||||
`device` VARCHAR(100) COMMENT '设备',
|
||||
`os` VARCHAR(100) COMMENT '操作系统',
|
||||
`browser` VARCHAR(100) COMMENT '浏览器',
|
||||
`status` TINYINT DEFAULT 1 COMMENT '0失败 1成功',
|
||||
`error_msg` VARCHAR(255) COMMENT '错误信息',
|
||||
`execution_time` INT COMMENT '执行时间(ms)',
|
||||
`create_time` DATETIME COMMENT '操作时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_module_action_time` (`module`, `action_type`, `create_time`),
|
||||
KEY `idx_operator_time` (`operator_id`, `create_time`),
|
||||
KEY `idx_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统操作日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for gen_table
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `gen_table`;
|
||||
CREATE TABLE `gen_table` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`table_name` varchar(100) NOT NULL COMMENT '表名',
|
||||
`module_name` varchar(100) COMMENT '模块名',
|
||||
`package_name` varchar(255) NOT NULL COMMENT '包名',
|
||||
`business_name` varchar(100) NOT NULL COMMENT '业务名',
|
||||
`entity_name` varchar(100) NOT NULL COMMENT '实体类名',
|
||||
`author` varchar(50) NOT NULL COMMENT '作者',
|
||||
`parent_menu_id` bigint COMMENT '上级菜单ID,对应sys_menu的id ',
|
||||
`remove_table_prefix` varchar(20) COMMENT '要移除的表前缀,如: sys_',
|
||||
`page_type` varchar(20) COMMENT '页面类型(classic|curd)',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint(4) DEFAULT 0 COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tablename` (`table_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代码生成配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for gen_table_column
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `gen_table_column`;
|
||||
CREATE TABLE `gen_table_column` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`table_id` bigint NOT NULL COMMENT '关联的表配置ID',
|
||||
`column_name` varchar(100) ,
|
||||
`column_type` varchar(50) ,
|
||||
`column_length` int ,
|
||||
`field_name` varchar(100) NOT NULL COMMENT '字段名称',
|
||||
`field_type` varchar(100) COMMENT '字段类型',
|
||||
`field_sort` int COMMENT '字段排序',
|
||||
`field_comment` varchar(255) COMMENT '字段描述',
|
||||
`max_length` int ,
|
||||
`is_required` tinyint(1) COMMENT '是否必填',
|
||||
`is_show_in_list` tinyint(1) DEFAULT '0' COMMENT '是否在列表显示',
|
||||
`is_show_in_form` tinyint(1) DEFAULT '0' COMMENT '是否在表单显示',
|
||||
`is_show_in_query` tinyint(1) DEFAULT '0' COMMENT '是否在查询条件显示',
|
||||
`query_type` tinyint COMMENT '查询方式',
|
||||
`form_type` tinyint COMMENT '表单类型',
|
||||
`dict_type` varchar(50) COMMENT '字典类型',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_table_id` (`table_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代码生成字段配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 系统配置表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_config`;
|
||||
CREATE TABLE `sys_config` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`config_name` varchar(50) NOT NULL COMMENT '配置名称',
|
||||
`config_key` varchar(50) NOT NULL COMMENT '配置key',
|
||||
`config_value` text NOT NULL COMMENT '配置值',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '更新人ID',
|
||||
`is_deleted` tinyint(4) DEFAULT '0' NOT NULL COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='系统配置表';
|
||||
|
||||
INSERT INTO `sys_config` VALUES (1, '系统限流QPS', 'IP_QPS_THRESHOLD_LIMIT', '10', '单个IP请求的最大每秒查询数(QPS)阈值Key', now(), 1, NULL, NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- 通知公告表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_notice`;
|
||||
CREATE TABLE `sys_notice` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(50) COMMENT '通知标题',
|
||||
`content` text COMMENT '通知内容',
|
||||
`type` tinyint NOT NULL COMMENT '通知类型(关联字典编码:notice_type)',
|
||||
`level` varchar(5) NOT NULL COMMENT '通知等级(字典code:notice_level)',
|
||||
`target_type` tinyint NOT NULL COMMENT '目标类型(1: 全体, 2: 指定)',
|
||||
`target_user_ids` varchar(255) COMMENT '目标人ID集合(多个使用英文逗号,分割)',
|
||||
`publisher_id` bigint COMMENT '发布人ID',
|
||||
`publish_status` tinyint DEFAULT '0' COMMENT '发布状态(0: 未发布, 1: 已发布, -1: 已撤回)',
|
||||
`publish_time` datetime COMMENT '发布时间',
|
||||
`revoke_time` datetime COMMENT '撤回时间',
|
||||
`create_by` bigint NOT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_by` bigint COMMENT '更新人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除(0: 未删除, 1: 已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统通知公告表';
|
||||
|
||||
INSERT INTO `sys_notice` VALUES (1, 'v3.0.0 版本发布 - 多租户功能上线', '<p>🎉 新版本发布,主要更新内容:</p><p>1. 新增多租户功能,支持租户隔离和数据管理</p><p>2. 优化系统性能,提升响应速度</p><p>3. 完善权限管理,增强安全性</p><p>4. 修复已知问题,提升系统稳定性</p>', 1, 'H', 1, NULL, 1, 1, '2024-12-15 10:00:00', NULL, 1, '2024-12-15 10:00:00', 1, '2024-12-15 10:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (2, '系统维护通知 - 2024年12月20日', '<p>⏰ 系统维护通知</p><p>系统将于 <strong>2024年12月20日(本周五)凌晨 2:00-4:00</strong> 进行例行维护升级。</p><p>维护期间系统将暂停服务,请提前做好数据备份工作。</p><p>给您带来的不便,敬请谅解!</p>', 2, 'H', 1, NULL, 1, 1, '2024-12-18 14:30:00', NULL, 1, '2024-12-18 14:30:00', 1, '2024-12-18 14:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (3, '安全提醒 - 防范钓鱼邮件', '<p>⚠️ 安全提醒</p><p>近期发现有不法分子通过钓鱼邮件进行网络攻击,请大家提高警惕:</p><p>1. 不要点击来源不明的邮件链接</p><p>2. 不要下载可疑附件</p><p>3. 遇到可疑邮件请及时联系IT部门</p><p>4. 定期修改密码,使用强密码策略</p>', 3, 'H', 1, NULL, 1, 1, '2024-12-10 09:00:00', NULL, 1, '2024-12-10 09:00:00', 1, '2024-12-10 09:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (4, '元旦假期安排通知', '<p>📅 元旦假期安排</p><p>根据国家法定节假日安排,公司元旦假期时间为:</p><p><strong>2024年12月30日(周一)至 2025年1月1日(周三)</strong>,共3天。</p><p>2024年12月29日(周日)正常上班。</p><p>祝大家元旦快乐,假期愉快!</p>', 4, 'M', 1, NULL, 1, 1, '2024-12-25 16:00:00', NULL, 1, '2024-12-25 16:00:00', 1, '2024-12-25 16:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (5, '新产品发布会邀请', '<p>🎊 新产品发布会邀请</p><p>公司将于 <strong>2025年1月15日下午14:00</strong> 在总部会议室举办新产品发布会。</p><p>届时将展示最新研发的产品和技术成果,欢迎全体员工参加。</p><p>请各部门提前安排好工作,准时参加。</p>', 5, 'M', 1, NULL, 1, 1, '2024-12-28 11:00:00', NULL, 1, '2024-12-28 11:00:00', 1, '2024-12-28 11:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (6, 'v2.16.1 版本更新', '<p>✨ 版本更新</p><p>v2.16.1 版本已发布,主要修复内容:</p><p>1. 修复 WebSocket 重复连接导致的后台线程阻塞问题</p><p>2. 优化通知公告功能,提升用户体验</p><p>3. 修复部分已知bug</p><p>建议尽快更新到最新版本。</p>', 1, 'M', 1, NULL, 1, 1, '2024-12-05 15:30:00', NULL, 1, '2024-12-05 15:30:00', 1, '2024-12-05 15:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (7, '年终总结会议通知', '<p>📋 年终总结会议通知</p><p>各部门年终总结会议将于 <strong>2024年12月30日上午9:00</strong> 召开。</p><p>请各部门负责人提前准备好年度工作总结和下年度工作计划。</p><p>会议地点:总部大会议室</p>', 5, 'M', 2, '1,2', 1, 1, '2024-12-22 10:00:00', NULL, 1, '2024-12-22 10:00:00', 1, '2024-12-22 10:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (8, '系统功能优化完成', '<p>✅ 系统功能优化</p><p>已完成以下功能优化:</p><p>1. 优化用户管理界面,提升操作体验</p><p>2. 增强数据导出功能,支持更多格式</p><p>3. 优化搜索功能,提升查询效率</p><p>4. 修复部分界面显示问题</p>', 1, 'L', 1, NULL, 1, 1, '2024-12-12 14:20:00', NULL, 1, '2024-12-12 14:20:00', 1, '2024-12-12 14:20:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (9, '员工培训计划', '<p>📚 员工培训计划</p><p>为提升员工专业技能,公司将于 <strong>2025年1月8日-10日</strong> 组织技术培训。</p><p>培训内容:</p><p>1. 新技术框架应用</p><p>2. 代码规范与最佳实践</p><p>3. 系统架构设计</p><p>请各部门合理安排工作,确保培训顺利进行。</p>', 5, 'M', 1, NULL, 1, 1, '2024-12-20 09:30:00', NULL, 1, '2024-12-20 09:30:00', 1, '2024-12-20 09:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (10, '数据备份提醒', '<p>💾 数据备份提醒</p><p>请各部门注意定期备份重要数据,建议每周至少备份一次。</p><p>备份方式:</p><p>1. 使用系统自带备份功能</p><p>2. 手动导出重要数据</p><p>3. 联系IT部门协助备份</p><p>数据安全,人人有责!</p>', 3, 'L', 1, NULL, 1, 1, '2024-12-08 08:00:00', NULL, 1, '2024-12-08 08:00:00', 1, '2024-12-08 08:00:00', 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- 用户通知公告表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user_notice`;
|
||||
CREATE TABLE `sys_user_notice` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`notice_id` bigint NOT NULL COMMENT '公共通知id',
|
||||
`user_id` bigint NOT NULL COMMENT '用户id',
|
||||
`is_read` tinyint DEFAULT '0' COMMENT '读取状态(0: 未读, 1: 已读)',
|
||||
`read_time` datetime COMMENT '阅读时间',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT '0' COMMENT '逻辑删除(0: 未删除, 1: 已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户通知公告关联表';
|
||||
|
||||
INSERT INTO `sys_user_notice` VALUES (1, 1, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (2, 2, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (3, 3, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (4, 4, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (5, 5, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (6, 6, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (7, 7, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (8, 8, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (9, 9, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (10, 10, 2, 1, NULL, now(), now(), 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user_social
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user_social`;
|
||||
CREATE TABLE `sys_user_social` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`platform` varchar(20) NOT NULL COMMENT '平台类型(WECHAT_MINI/WECHAT_MP/ALIPAY/QQ/APPLE)',
|
||||
`openid` varchar(64) NOT NULL COMMENT '平台openid',
|
||||
`unionid` varchar(64) DEFAULT NULL COMMENT '微信unionid',
|
||||
`nickname` varchar(64) DEFAULT NULL COMMENT '第三方昵称',
|
||||
`avatar` varchar(255) DEFAULT NULL COMMENT '第三方头像URL',
|
||||
`session_key` varchar(128) DEFAULT NULL COMMENT '微信session_key',
|
||||
`verified` tinyint(1) DEFAULT 1 COMMENT '是否已验证(1-已验证 0-未验证)',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '绑定时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_platform_openid` (`platform`, `openid`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_unionid` (`unionid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户第三方账号绑定表';
|
||||
@@ -0,0 +1,524 @@
|
||||
# YouLai_Admin 数据库(MySQL 5.7 ~ MySQL 8.x)
|
||||
# Copyright (c) 2021-present, youlai.tech
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 1. 创建数据库
|
||||
-- ----------------------------
|
||||
CREATE DATABASE IF NOT EXISTS youlai_admin_template CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 2. 创建表 && 数据初始化
|
||||
-- ----------------------------
|
||||
USE youlai_admin_template;
|
||||
|
||||
SET NAMES utf8mb4; # 设置字符集
|
||||
SET FOREIGN_KEY_CHECKS = 0; # 关闭外键检查,加快导入速度
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dept
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dept`;
|
||||
CREATE TABLE `sys_dept` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`name` varchar(100) NOT NULL COMMENT '部门名称',
|
||||
`code` varchar(100) NOT NULL COMMENT '部门编号',
|
||||
`parent_id` bigint DEFAULT 0 COMMENT '父节点id',
|
||||
`tree_path` varchar(255) NOT NULL COMMENT '父节点id路径',
|
||||
`sort` smallint DEFAULT 0 COMMENT '显示顺序',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`create_by` bigint NULL COMMENT '创建人ID',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_by` bigint NULL COMMENT '修改人ID',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除标识(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_code`(`code` ASC) USING BTREE COMMENT '部门编号唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '部门管理表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_dept
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dept` VALUES (1, '有来技术', 'YOULAI', 0, '0', 1, 1, 1, NULL, 1, now(), 0);
|
||||
INSERT INTO `sys_dept` VALUES (2, '研发部门', 'RD001', 1, '0,1', 1, 1, 2, NULL, 2, now(), 0);
|
||||
INSERT INTO `sys_dept` VALUES (3, '测试部门', 'QA001', 1, '0,1', 1, 1, 2, NULL, 2, now(), 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dict
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dict`;
|
||||
CREATE TABLE `sys_dict` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键 ',
|
||||
`dict_code` varchar(50) COMMENT '类型编码',
|
||||
`name` varchar(50) COMMENT '类型名称',
|
||||
`status` tinyint(1) DEFAULT '0' COMMENT '状态(0:正常;1:禁用)',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
`is_deleted` tinyint DEFAULT '0' COMMENT '是否删除(1-删除,0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_dict_code` (`dict_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典类型表';
|
||||
-- ----------------------------
|
||||
-- Records of sys_dict
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dict` VALUES (1, 'gender', '性别', 1, NULL, now() , 1,now(), 1,0);
|
||||
INSERT INTO `sys_dict` VALUES (2, 'notice_type', '通知类型', 1, NULL, now(), 1,now(), 1,0);
|
||||
INSERT INTO `sys_dict` VALUES (3, 'notice_level', '通知级别', 1, NULL, now(), 1,now(), 1,0);
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dict_item
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dict_item`;
|
||||
CREATE TABLE `sys_dict_item` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`dict_code` varchar(50) COMMENT '关联字典编码,与sys_dict表中的dict_code对应',
|
||||
`value` varchar(50) COMMENT '字典项值',
|
||||
`label` varchar(100) COMMENT '字典项标签',
|
||||
`tag_type` varchar(50) COMMENT '标签类型,用于前端样式展示(如success、warning等)',
|
||||
`status` tinyint DEFAULT '0' COMMENT '状态(1-正常,0-禁用)',
|
||||
`sort` int DEFAULT '0' COMMENT '排序',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典项表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_dict_item
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dict_item` VALUES (1, 'gender', '1', '男', 'primary', 1, 1, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (2, 'gender', '2', '女', 'danger', 1, 2, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (3, 'gender', '0', '保密', 'info', 1, 3, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (4, 'notice_type', '1', '系统升级', 'success', 1, 1, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (5, 'notice_type', '2', '系统维护', 'primary', 1, 2, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (6, 'notice_type', '3', '安全警告', 'danger', 1, 3, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (7, 'notice_type', '4', '假期通知', 'success', 1, 4, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (8, 'notice_type', '5', '公司新闻', 'primary', 1, 5, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (9, 'notice_type', '99', '其他', 'info', 1, 99, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (10, 'notice_level', 'L', '低', 'info', 1, 1, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (11, 'notice_level', 'M', '中', 'warning', 1, 2, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (12, 'notice_level', 'H', '高', 'danger', 1, 3, '', now(), 1,now(),1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_menu
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_menu`;
|
||||
CREATE TABLE `sys_menu` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`parent_id` bigint NOT NULL COMMENT '父菜单ID',
|
||||
`tree_path` varchar(255) COMMENT '父节点ID路径',
|
||||
`name` varchar(64) NOT NULL COMMENT '菜单名称',
|
||||
`type` char(1) NOT NULL COMMENT '菜单类型(C-目录 M-菜单 B-按钮)',
|
||||
`route_name` varchar(255) COMMENT '路由名称(Vue Router 中用于命名路由)',
|
||||
`route_path` varchar(128) COMMENT '路由路径(Vue Router 中定义的 URL 路径)',
|
||||
`component` varchar(128) COMMENT '组件路径(组件页面完整路径,相对于 src/views/,缺省后缀 .vue)',
|
||||
`perm` varchar(128) COMMENT '【按钮】权限标识',
|
||||
`always_show` tinyint DEFAULT 0 COMMENT '【目录】只有一个子路由是否始终显示(1-是 0-否)',
|
||||
`keep_alive` tinyint DEFAULT 0 COMMENT '【菜单】是否开启页面缓存(1-是 0-否)',
|
||||
`visible` tinyint(1) DEFAULT 1 COMMENT '显示状态(1-显示 0-隐藏)',
|
||||
`sort` int DEFAULT 0 COMMENT '排序',
|
||||
`icon` varchar(64) COMMENT '菜单图标',
|
||||
`redirect` varchar(128) COMMENT '跳转路径',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`params` varchar(255) NULL COMMENT '路由参数',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统菜单表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_menu
|
||||
-- ----------------------------
|
||||
-- 顶级目录:系统管理/代码生成/平台文档/接口文档
|
||||
INSERT INTO `sys_menu` VALUES (1, 0, '0', '系统管理', 'C', '', '/system', 'Layout', NULL, NULL, NULL, 1, 1, 'system', '/system/user', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2, 0, '0', '代码生成', 'C', '', '/codegen', 'Layout', NULL, NULL, NULL, 1, 2, 'code', '/codegen/index', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (4, 0, '0', '平台文档', 'C', '', '/doc', 'Layout', NULL, NULL, NULL, 1, 4, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (5, 0, '0', '接口文档', 'C', '', '/api', 'Layout', NULL, NULL, NULL, 1, 5, 'api', '', now(), now(), NULL);
|
||||
|
||||
-- 系统管理
|
||||
INSERT INTO `sys_menu` VALUES (210, 1, '0,1', '用户管理', 'M', 'User', 'user', 'system/user/index', NULL, NULL, 1, 1, 1, 'el-icon-User', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2101, 210, '0,1,210', '用户查询', 'B', NULL, '', NULL, 'sys:user:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2102, 210, '0,1,210', '用户新增', 'B', NULL, '', NULL, 'sys:user:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2103, 210, '0,1,210', '用户编辑', 'B', NULL, '', NULL, 'sys:user:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2104, 210, '0,1,210', '用户删除', 'B', NULL, '', NULL, 'sys:user:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2105, 210, '0,1,210', '重置密码', 'B', NULL, '', NULL, 'sys:user:reset-password', NULL, NULL, 1, 5, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2106, 210, '0,1,210', '用户导入', 'B', NULL, '', NULL, 'sys:user:import', NULL, NULL, 1, 6, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2107, 210, '0,1,210', '用户导出', 'B', NULL, '', NULL, 'sys:user:export', NULL, NULL, 1, 7, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (220, 1, '0,1', '角色管理', 'M', 'Role', 'role', 'system/role/index', NULL, NULL, 1, 1, 2, 'role', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2201, 220, '0,1,220', '角色查询', 'B', NULL, '', NULL, 'sys:role:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2202, 220, '0,1,220', '角色新增', 'B', NULL, '', NULL, 'sys:role:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2203, 220, '0,1,220', '角色编辑', 'B', NULL, '', NULL, 'sys:role:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2204, 220, '0,1,220', '角色删除', 'B', NULL, '', NULL, 'sys:role:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2205, 220, '0,1,220', '角色分配权限', 'B', NULL, '', NULL, 'sys:role:assign', NULL, NULL, 1, 5, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (230, 1, '0,1', '菜单管理', 'M', 'SysMenu', 'menu', 'system/menu/index', NULL, NULL, 1, 1, 3, 'menu', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2301, 230, '0,1,230', '菜单查询', 'B', NULL, '', NULL, 'sys:menu:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2302, 230, '0,1,230', '菜单新增', 'B', NULL, '', NULL, 'sys:menu:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2303, 230, '0,1,230', '菜单编辑', 'B', NULL, '', NULL, 'sys:menu:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2304, 230, '0,1,230', '菜单删除', 'B', NULL, '', NULL, 'sys:menu:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (240, 1, '0,1', '部门管理', 'M', 'Dept', 'dept', 'system/dept/index', NULL, NULL, 1, 1, 4, 'tree', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2401, 240, '0,1,240', '部门查询', 'B', NULL, '', NULL, 'sys:dept:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2402, 240, '0,1,240', '部门新增', 'B', NULL, '', NULL, 'sys:dept:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2403, 240, '0,1,240', '部门编辑', 'B', NULL, '', NULL, 'sys:dept:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2404, 240, '0,1,240', '部门删除', 'B', NULL, '', NULL, 'sys:dept:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (250, 1, '0,1', '字典管理', 'M', 'Dict', 'dict', 'system/dict/index', NULL, NULL, 1, 1, 5, 'dict', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2501, 250, '0,1,250', '字典查询', 'B', NULL, '', NULL, 'sys:dict:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2502, 250, '0,1,250', '字典新增', 'B', NULL, '', NULL, 'sys:dict:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2503, 250, '0,1,250', '字典编辑', 'B', NULL, '', NULL, 'sys:dict:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2504, 250, '0,1,250', '字典删除', 'B', NULL, '', NULL, 'sys:dict:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (251, 1, '0,1', '字典项', 'M', 'DictItem', 'dict-item', 'system/dict/dict-item', NULL, 0, 1, 0, 6, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2511, 251, '0,1,251', '字典项查询', 'B', NULL, '', NULL, 'sys:dict-item:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2512, 251, '0,1,251', '字典项新增', 'B', NULL, '', NULL, 'sys:dict-item:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2513, 251, '0,1,251', '字典项编辑', 'B', NULL, '', NULL, 'sys:dict-item:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2514, 251, '0,1,251', '字典项删除', 'B', NULL, '', NULL, 'sys:dict-item:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (260, 1, '0,1', '系统日志', 'M', 'Log', 'log', 'system/log/index', NULL, 0, 1, 1, 7, 'document', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (270, 1, '0,1', '系统配置', 'M', 'Config', 'config', 'system/config/index', NULL, 0, 1, 1, 8, 'setting', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2701, 270, '0,1,270', '系统配置查询', 'B', NULL, '', NULL, 'sys:config:list', 0, 1, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2702, 270, '0,1,270', '系统配置新增', 'B', NULL, '', NULL, 'sys:config:create', 0, 1, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2703, 270, '0,1,270', '系统配置修改', 'B', NULL, '', NULL, 'sys:config:update', 0, 1, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2704, 270, '0,1,270', '系统配置删除', 'B', NULL, '', NULL, 'sys:config:delete', 0, 1, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2705, 270, '0,1,270', '系统配置刷新', 'B', NULL, '', NULL, 'sys:config:refresh', 0, 1, 1, 5, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (280, 1, '0,1', '通知公告', 'M', 'Notice', 'notice', 'system/notice/index', NULL, NULL, NULL, 1, 9, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2801, 280, '0,1,280', '通知查询', 'B', NULL, '', NULL, 'sys:notice:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2802, 280, '0,1,280', '通知新增', 'B', NULL, '', NULL, 'sys:notice:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2803, 280, '0,1,280', '通知编辑', 'B', NULL, '', NULL, 'sys:notice:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2804, 280, '0,1,280', '通知删除', 'B', NULL, '', NULL, 'sys:notice:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2805, 280, '0,1,280', '通知发布', 'B', NULL, '', NULL, 'sys:notice:publish', 0, 1, 1, 5, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2806, 280, '0,1,280', '通知撤回', 'B', NULL, '', NULL, 'sys:notice:revoke', 0, 1, 1, 6, '', NULL, now(), now(), NULL);
|
||||
|
||||
-- 代码生成
|
||||
INSERT INTO `sys_menu` VALUES (310, 2, '0,2', '代码生成', 'M', 'Codegen', 'codegen', 'codegen/index', NULL, NULL, 1, 1, 1, 'code', NULL, now(), now(), NULL);
|
||||
|
||||
-- 平台文档(外链通过 route_path 识别)
|
||||
INSERT INTO `sys_menu` VALUES (501, 4, '0,4', '平台文档(外链)', 'M', NULL, 'https://juejin.cn/post/7228990409909108793', '', NULL, NULL, NULL, 1, 1, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (502, 4, '0,4', '后端文档', 'M', NULL, 'https://youlai.blog.csdn.net/article/details/145178880', '', NULL, NULL, NULL, 1, 2, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (503, 4, '0,4', '移动端文档', 'M', NULL, 'https://youlai.blog.csdn.net/article/details/143222890', '', NULL, NULL, NULL, 1, 3, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (504, 4, '0,4', '内部文档', 'M', NULL, 'internal-doc', 'demo/internal-doc', NULL, NULL, NULL, 1, 4, 'document', '', now(), now(), NULL);
|
||||
|
||||
-- 接口文档
|
||||
INSERT INTO `sys_menu` VALUES (601, 5, '0,5', 'Apifox', 'M', 'Apifox', 'apifox', 'demo/api/apifox', NULL, NULL, 1, 1, 1, 'api', '', now(), now(), NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role`;
|
||||
CREATE TABLE `sys_role` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(64) NOT NULL COMMENT '角色名称',
|
||||
`code` varchar(32) NOT NULL COMMENT '角色编码',
|
||||
`sort` int NULL COMMENT '显示顺序',
|
||||
`status` tinyint(1) DEFAULT 1 COMMENT '角色状态(1-正常 0-停用)',
|
||||
`data_scope` tinyint NULL COMMENT '数据权限(1-所有数据 2-部门及子部门数据 3-本部门数据 4-本人数据 5-自定义部门数据)',
|
||||
`create_by` bigint NULL COMMENT '创建人 ID',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_by` bigint NULL COMMENT '更新人ID',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint(1) DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_name`(`name` ASC) USING BTREE COMMENT '角色名称唯一索引',
|
||||
UNIQUE INDEX `uk_code`(`code` ASC) USING BTREE COMMENT '角色编码唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统角色表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_role
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_role` VALUES (1, '超级管理员', 'ROOT', 1, 1, 1, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (2, '系统管理员', 'ADMIN', 2, 1, 1, NULL, now(), NULL, NULL, 0);
|
||||
INSERT INTO `sys_role` VALUES (3, '访问游客', 'GUEST', 3, 1, 3, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (4, '部门主管', 'DEPT_MANAGER', 4, 1, 2, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (5, '部门成员', 'DEPT_MEMBER', 5, 1, 3, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (6, '普通员工', 'EMPLOYEE', 6, 1, 4, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (7, '自定义权限用户', 'CUSTOM_USER', 7, 1, 5, NULL, now(), NULL, now(), 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role_menu
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role_menu`;
|
||||
CREATE TABLE `sys_role_menu` (
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
`menu_id` bigint NOT NULL COMMENT '菜单ID',
|
||||
UNIQUE INDEX `uk_roleid_menuid`(`role_id` ASC, `menu_id` ASC) USING BTREE COMMENT '角色菜单唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '角色菜单关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role_dept
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role_dept`;
|
||||
CREATE TABLE `sys_role_dept` (
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
`dept_id` bigint NOT NULL COMMENT '部门ID',
|
||||
UNIQUE INDEX `uk_roleid_deptid`(`role_id` ASC, `dept_id` ASC) USING BTREE COMMENT '角色部门唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '角色部门关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_role_dept
|
||||
-- ----------------------------
|
||||
INSERT IGNORE INTO `sys_role_dept` VALUES (7, 1);
|
||||
INSERT IGNORE INTO `sys_role_dept` VALUES (7, 2);
|
||||
|
||||
-- ============================================
|
||||
-- 系统管理员角色菜单权限(role_id=2)
|
||||
-- 顶级目录
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 1), (2, 2), (2, 4), (2, 5);
|
||||
-- 系统管理
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 210), (2, 2101), (2, 2102), (2, 2103), (2, 2104), (2, 2105), (2, 2106), (2, 2107);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 220), (2, 2201), (2, 2202), (2, 2203), (2, 2204), (2, 2205);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 230), (2, 2301), (2, 2302), (2, 2303), (2, 2304);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 240), (2, 2401), (2, 2402), (2, 2403), (2, 2404);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 250), (2, 2501), (2, 2502), (2, 2503), (2, 2504);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 251), (2, 2511), (2, 2512), (2, 2513), (2, 2514);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 260), (2, 2601);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 270), (2, 2701), (2, 2702), (2, 2703), (2, 2704), (2, 2705);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 280), (2, 2801), (2, 2802), (2, 2803), (2, 2804), (2, 2805), (2, 2806);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 210), (4, 2101), (4, 2102), (4, 2103), (4, 2104), (4, 2105), (4, 2106), (4, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 220), (4, 2201), (4, 2202), (4, 2203), (4, 2204), (4, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 210), (5, 2101), (5, 2102), (5, 2103), (5, 2104), (5, 2105), (5, 2106), (5, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 220), (5, 2201), (5, 2202), (5, 2203), (5, 2204), (5, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 210), (6, 2101), (6, 2102), (6, 2103), (6, 2104), (6, 2105), (6, 2106), (6, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 220), (6, 2201), (6, 2202), (6, 2203), (6, 2204), (6, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 210), (7, 2101), (7, 2102), (7, 2103), (7, 2104), (7, 2105), (7, 2106), (7, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 220), (7, 2201), (7, 2202), (7, 2203), (7, 2204), (7, 2205);
|
||||
-- 代码生成
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 310);
|
||||
-- 平台文档
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 501), (2, 502), (2, 503), (2, 504);
|
||||
-- 接口文档
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 601);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user`;
|
||||
CREATE TABLE `sys_user` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(64) COMMENT '用户名',
|
||||
`nickname` varchar(64) COMMENT '昵称',
|
||||
`gender` tinyint(1) DEFAULT 1 COMMENT '性别((1-男 2-女 0-保密)',
|
||||
`password` varchar(100) COMMENT '密码',
|
||||
`dept_id` int COMMENT '部门ID',
|
||||
`avatar` varchar(255) COMMENT '用户头像',
|
||||
`mobile` varchar(20) COMMENT '联系方式',
|
||||
`status` tinyint(1) DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`email` varchar(128) COMMENT '用户邮箱',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
`is_deleted` tinyint(1) DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统用户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_user` VALUES (1, 'root', '有来技术', 0, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', NULL, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345677', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (2, 'admin', '系统管理员', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18888888888', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (3, 'test', '测试小用户', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 3, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345679', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (4, 'dept_manager', '部门主管', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345680', 1, 'manager@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (5, 'dept_member', '部门成员', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345681', 1, 'member@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (6, 'employee', '普通员工', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 2, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345682', 1, 'employee@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (7, 'custom_user', '自定义权限用户', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 3, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345683', 1, 'custom@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user_role
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user_role`;
|
||||
CREATE TABLE `sys_user_role` (
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
PRIMARY KEY (`user_id`, `role_id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '用户角色关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_user_role
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_user_role` VALUES (1, 1);
|
||||
INSERT INTO `sys_user_role` VALUES (2, 2);
|
||||
INSERT INTO `sys_user_role` VALUES (3, 3);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (4, 4);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (5, 5);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (6, 6);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (7, 7);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_log`;
|
||||
CREATE TABLE `sys_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`module` TINYINT NOT NULL COMMENT '模块,数字枚举,参考 LogModule 枚举',
|
||||
`action_type` TINYINT NOT NULL COMMENT '操作类型,数字枚举,参考 ActionType 枚举',
|
||||
`title` VARCHAR(100) NOT NULL COMMENT '前端显示标题',
|
||||
`content` TEXT COMMENT '自定义日志内容',
|
||||
`operator_id` BIGINT COMMENT '操作人ID',
|
||||
`operator_name` VARCHAR(50) COMMENT '操作人名称',
|
||||
`request_uri` VARCHAR(255) COMMENT '请求路径',
|
||||
`request_method` VARCHAR(10) COMMENT '请求方法',
|
||||
`ip` VARCHAR(45) COMMENT 'IP地址',
|
||||
`province` VARCHAR(100) COMMENT '省份',
|
||||
`city` VARCHAR(100) COMMENT '城市',
|
||||
`device` VARCHAR(100) COMMENT '设备',
|
||||
`os` VARCHAR(100) COMMENT '操作系统',
|
||||
`browser` VARCHAR(100) COMMENT '浏览器',
|
||||
`status` TINYINT DEFAULT 1 COMMENT '0失败 1成功',
|
||||
`error_msg` VARCHAR(255) COMMENT '错误信息',
|
||||
`execution_time` INT COMMENT '执行时间(ms)',
|
||||
`create_time` DATETIME COMMENT '操作时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_module_action_time` (`module`, `action_type`, `create_time`),
|
||||
KEY `idx_operator_time` (`operator_id`, `create_time`),
|
||||
KEY `idx_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统操作日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for gen_table
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `gen_table`;
|
||||
CREATE TABLE `gen_table` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`table_name` varchar(100) NOT NULL COMMENT '表名',
|
||||
`module_name` varchar(100) COMMENT '模块名',
|
||||
`package_name` varchar(255) NOT NULL COMMENT '包名',
|
||||
`business_name` varchar(100) NOT NULL COMMENT '业务名',
|
||||
`entity_name` varchar(100) NOT NULL COMMENT '实体类名',
|
||||
`author` varchar(50) NOT NULL COMMENT '作者',
|
||||
`parent_menu_id` bigint COMMENT '上级菜单ID,对应sys_menu的id ',
|
||||
`remove_table_prefix` varchar(20) COMMENT '要移除的表前缀,如: sys_',
|
||||
`page_type` varchar(20) COMMENT '页面类型(classic|curd)',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint(4) DEFAULT 0 COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tablename` (`table_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代码生成配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for gen_table_column
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `gen_table_column`;
|
||||
CREATE TABLE `gen_table_column` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`table_id` bigint NOT NULL COMMENT '关联的表配置ID',
|
||||
`column_name` varchar(100) ,
|
||||
`column_type` varchar(50) ,
|
||||
`column_length` int ,
|
||||
`field_name` varchar(100) NOT NULL COMMENT '字段名称',
|
||||
`field_type` varchar(100) COMMENT '字段类型',
|
||||
`field_sort` int COMMENT '字段排序',
|
||||
`field_comment` varchar(255) COMMENT '字段描述',
|
||||
`max_length` int ,
|
||||
`is_required` tinyint(1) COMMENT '是否必填',
|
||||
`is_show_in_list` tinyint(1) DEFAULT '0' COMMENT '是否在列表显示',
|
||||
`is_show_in_form` tinyint(1) DEFAULT '0' COMMENT '是否在表单显示',
|
||||
`is_show_in_query` tinyint(1) DEFAULT '0' COMMENT '是否在查询条件显示',
|
||||
`query_type` tinyint COMMENT '查询方式',
|
||||
`form_type` tinyint COMMENT '表单类型',
|
||||
`dict_type` varchar(50) COMMENT '字典类型',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_table_id` (`table_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代码生成字段配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 系统配置表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_config`;
|
||||
CREATE TABLE `sys_config` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`config_name` varchar(50) NOT NULL COMMENT '配置名称',
|
||||
`config_key` varchar(50) NOT NULL COMMENT '配置key',
|
||||
`config_value` text NOT NULL COMMENT '配置值',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '更新人ID',
|
||||
`is_deleted` tinyint(4) DEFAULT '0' NOT NULL COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='系统配置表';
|
||||
|
||||
INSERT INTO `sys_config` VALUES (1, '系统限流QPS', 'IP_QPS_THRESHOLD_LIMIT', '10', '单个IP请求的最大每秒查询数(QPS)阈值Key', now(), 1, NULL, NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- 通知公告表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_notice`;
|
||||
CREATE TABLE `sys_notice` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(50) COMMENT '通知标题',
|
||||
`content` text COMMENT '通知内容',
|
||||
`type` tinyint NOT NULL COMMENT '通知类型(关联字典编码:notice_type)',
|
||||
`level` varchar(5) NOT NULL COMMENT '通知等级(字典code:notice_level)',
|
||||
`target_type` tinyint NOT NULL COMMENT '目标类型(1: 全体, 2: 指定)',
|
||||
`target_user_ids` varchar(255) COMMENT '目标人ID集合(多个使用英文逗号,分割)',
|
||||
`publisher_id` bigint COMMENT '发布人ID',
|
||||
`publish_status` tinyint DEFAULT '0' COMMENT '发布状态(0: 未发布, 1: 已发布, -1: 已撤回)',
|
||||
`publish_time` datetime COMMENT '发布时间',
|
||||
`revoke_time` datetime COMMENT '撤回时间',
|
||||
`create_by` bigint NOT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_by` bigint COMMENT '更新人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除(0: 未删除, 1: 已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统通知公告表';
|
||||
|
||||
INSERT INTO `sys_notice` VALUES (1, 'v3.0.0 版本发布 - 多租户功能上线', '<p>🎉 新版本发布,主要更新内容:</p><p>1. 新增多租户功能,支持租户隔离和数据管理</p><p>2. 优化系统性能,提升响应速度</p><p>3. 完善权限管理,增强安全性</p><p>4. 修复已知问题,提升系统稳定性</p>', 1, 'H', 1, NULL, 1, 1, '2024-12-15 10:00:00', NULL, 1, '2024-12-15 10:00:00', 1, '2024-12-15 10:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (2, '系统维护通知 - 2024年12月20日', '<p>⏰ 系统维护通知</p><p>系统将于 <strong>2024年12月20日(本周五)凌晨 2:00-4:00</strong> 进行例行维护升级。</p><p>维护期间系统将暂停服务,请提前做好数据备份工作。</p><p>给您带来的不便,敬请谅解!</p>', 2, 'H', 1, NULL, 1, 1, '2024-12-18 14:30:00', NULL, 1, '2024-12-18 14:30:00', 1, '2024-12-18 14:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (3, '安全提醒 - 防范钓鱼邮件', '<p>⚠️ 安全提醒</p><p>近期发现有不法分子通过钓鱼邮件进行网络攻击,请大家提高警惕:</p><p>1. 不要点击来源不明的邮件链接</p><p>2. 不要下载可疑附件</p><p>3. 遇到可疑邮件请及时联系IT部门</p><p>4. 定期修改密码,使用强密码策略</p>', 3, 'H', 1, NULL, 1, 1, '2024-12-10 09:00:00', NULL, 1, '2024-12-10 09:00:00', 1, '2024-12-10 09:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (4, '元旦假期安排通知', '<p>📅 元旦假期安排</p><p>根据国家法定节假日安排,公司元旦假期时间为:</p><p><strong>2024年12月30日(周一)至 2025年1月1日(周三)</strong>,共3天。</p><p>2024年12月29日(周日)正常上班。</p><p>祝大家元旦快乐,假期愉快!</p>', 4, 'M', 1, NULL, 1, 1, '2024-12-25 16:00:00', NULL, 1, '2024-12-25 16:00:00', 1, '2024-12-25 16:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (5, '新产品发布会邀请', '<p>🎊 新产品发布会邀请</p><p>公司将于 <strong>2025年1月15日下午14:00</strong> 在总部会议室举办新产品发布会。</p><p>届时将展示最新研发的产品和技术成果,欢迎全体员工参加。</p><p>请各部门提前安排好工作,准时参加。</p>', 5, 'M', 1, NULL, 1, 1, '2024-12-28 11:00:00', NULL, 1, '2024-12-28 11:00:00', 1, '2024-12-28 11:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (6, 'v2.16.1 版本更新', '<p>✨ 版本更新</p><p>v2.16.1 版本已发布,主要修复内容:</p><p>1. 修复 WebSocket 重复连接导致的后台线程阻塞问题</p><p>2. 优化通知公告功能,提升用户体验</p><p>3. 修复部分已知bug</p><p>建议尽快更新到最新版本。</p>', 1, 'M', 1, NULL, 1, 1, '2024-12-05 15:30:00', NULL, 1, '2024-12-05 15:30:00', 1, '2024-12-05 15:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (7, '年终总结会议通知', '<p>📋 年终总结会议通知</p><p>各部门年终总结会议将于 <strong>2024年12月30日上午9:00</strong> 召开。</p><p>请各部门负责人提前准备好年度工作总结和下年度工作计划。</p><p>会议地点:总部大会议室</p>', 5, 'M', 2, '1,2', 1, 1, '2024-12-22 10:00:00', NULL, 1, '2024-12-22 10:00:00', 1, '2024-12-22 10:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (8, '系统功能优化完成', '<p>✅ 系统功能优化</p><p>已完成以下功能优化:</p><p>1. 优化用户管理界面,提升操作体验</p><p>2. 增强数据导出功能,支持更多格式</p><p>3. 优化搜索功能,提升查询效率</p><p>4. 修复部分界面显示问题</p>', 1, 'L', 1, NULL, 1, 1, '2024-12-12 14:20:00', NULL, 1, '2024-12-12 14:20:00', 1, '2024-12-12 14:20:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (9, '员工培训计划', '<p>📚 员工培训计划</p><p>为提升员工专业技能,公司将于 <strong>2025年1月8日-10日</strong> 组织技术培训。</p><p>培训内容:</p><p>1. 新技术框架应用</p><p>2. 代码规范与最佳实践</p><p>3. 系统架构设计</p><p>请各部门合理安排工作,确保培训顺利进行。</p>', 5, 'M', 1, NULL, 1, 1, '2024-12-20 09:30:00', NULL, 1, '2024-12-20 09:30:00', 1, '2024-12-20 09:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (10, '数据备份提醒', '<p>💾 数据备份提醒</p><p>请各部门注意定期备份重要数据,建议每周至少备份一次。</p><p>备份方式:</p><p>1. 使用系统自带备份功能</p><p>2. 手动导出重要数据</p><p>3. 联系IT部门协助备份</p><p>数据安全,人人有责!</p>', 3, 'L', 1, NULL, 1, 1, '2024-12-08 08:00:00', NULL, 1, '2024-12-08 08:00:00', 1, '2024-12-08 08:00:00', 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- 用户通知公告表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user_notice`;
|
||||
CREATE TABLE `sys_user_notice` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`notice_id` bigint NOT NULL COMMENT '公共通知id',
|
||||
`user_id` bigint NOT NULL COMMENT '用户id',
|
||||
`is_read` bigint DEFAULT '0' COMMENT '读取状态(0: 未读, 1: 已读)',
|
||||
`read_time` datetime COMMENT '阅读时间',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT '0' COMMENT '逻辑删除(0: 未删除, 1: 已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户通知公告关联表';
|
||||
|
||||
INSERT INTO `sys_user_notice` VALUES (1, 1, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (2, 2, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (3, 3, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (4, 4, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (5, 5, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (6, 6, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (7, 7, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (8, 8, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (9, 9, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (10, 10, 2, 1, NULL, now(), now(), 0);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
package com.youlai.boot;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* 应用启动类
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 0.0.1
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class YouLaiBootApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(YouLaiBootApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.youlai.boot.auth.controller;
|
||||
|
||||
import com.youlai.boot.auth.model.LoginReq;
|
||||
import com.youlai.boot.common.enums.ActionTypeEnum;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
import com.youlai.boot.common.result.Result;
|
||||
import com.youlai.boot.auth.service.AuthService;
|
||||
import com.youlai.boot.common.annotation.Log;
|
||||
import com.youlai.boot.framework.captcha.model.CaptchaInfo;
|
||||
import com.youlai.boot.framework.security.model.AuthenticationToken;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 认证控制层
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 0.0.1
|
||||
*/
|
||||
@Tag(name = "01.认证中心")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
|
||||
@Operation(summary = "获取验证码")
|
||||
@GetMapping("/captcha")
|
||||
public Result<CaptchaInfo> getCaptcha() {
|
||||
CaptchaInfo captcha = authService.getCaptcha();
|
||||
return Result.success(captcha);
|
||||
}
|
||||
|
||||
@Operation(summary = "账号密码登录")
|
||||
@PostMapping("/login")
|
||||
@Log(module = LogModuleEnum.LOGIN, value = ActionTypeEnum.LOGIN)
|
||||
public Result<AuthenticationToken> login(@RequestBody @Valid LoginReq request) {
|
||||
AuthenticationToken authenticationToken = authService.login(request.getUsername(), request.getPassword());
|
||||
return Result.success(authenticationToken);
|
||||
}
|
||||
|
||||
@Operation(summary = "短信验证码登录")
|
||||
@PostMapping("/login/sms")
|
||||
@Log(module = LogModuleEnum.LOGIN, value = ActionTypeEnum.LOGIN)
|
||||
public Result<AuthenticationToken> loginBySms(
|
||||
@Parameter(description = "手机号", example = "18888888888") @RequestParam String mobile,
|
||||
@Parameter(description = "验证码", example = "123456") @RequestParam String code
|
||||
) {
|
||||
AuthenticationToken loginResult = authService.loginBySms(mobile, code);
|
||||
return Result.success(loginResult);
|
||||
}
|
||||
|
||||
@Operation(summary = "发送登录短信验证码")
|
||||
@PostMapping("/sms/code")
|
||||
public Result<Void> sendSmsCode(
|
||||
@Parameter(description = "手机号", example = "18888888888") @RequestParam String mobile
|
||||
) {
|
||||
authService.sendSmsCode(mobile);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "退出登录")
|
||||
@DeleteMapping("/logout")
|
||||
@Log(module = LogModuleEnum.LOGIN, value = ActionTypeEnum.LOGOUT)
|
||||
public Result<Void> logout() {
|
||||
authService.logout();
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "刷新令牌")
|
||||
@PostMapping("/refresh-token")
|
||||
public Result<AuthenticationToken> refreshToken(
|
||||
@Parameter(description = "刷新令牌", example = "xxx.xxx.xxx") @RequestParam String refreshToken
|
||||
) {
|
||||
AuthenticationToken authenticationToken = authService.refreshToken(refreshToken);
|
||||
return Result.success(authenticationToken);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.youlai.boot.auth.controller;
|
||||
|
||||
import com.youlai.boot.auth.model.WxMaBindMobileReq;
|
||||
import com.youlai.boot.auth.model.WxMaPhoneLoginReq;
|
||||
import com.youlai.boot.auth.model.WxMaLoginResp;
|
||||
import com.youlai.boot.auth.service.WxMaAuthService;
|
||||
import com.youlai.boot.common.annotation.Log;
|
||||
import com.youlai.boot.common.enums.ActionTypeEnum;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
import com.youlai.boot.common.result.Result;
|
||||
import com.youlai.boot.framework.security.model.AuthenticationToken;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
* 微信小程序认证控制层
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@Tag(name = "13.微信小程序认证")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/wxma/auth")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class WxMaAuthController {
|
||||
|
||||
private final WxMaAuthService wxMaAuthService;
|
||||
|
||||
/**
|
||||
* 静默登录
|
||||
* <p>
|
||||
* 适用场景:个人小程序、无需手机号的登录场景
|
||||
* <ul>
|
||||
* <li>已绑定手机号的用户:直接返回 token,登录成功</li>
|
||||
* <li>未绑定手机号的用户:返回 openid,需调用绑定手机号接口</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Operation(summary = "静默登录", description = "通过微信 code 登录,已绑定用户直接返回 token,未绑定用户返回 openid 需绑定手机号")
|
||||
@PostMapping("/silent-login")
|
||||
@Log(module = LogModuleEnum.LOGIN, value = ActionTypeEnum.LOGIN)
|
||||
public Result<WxMaLoginResp> silentLogin(
|
||||
@Parameter(description = "微信登录凭证(wx.login 获取)", required = true, example = "0xxx")
|
||||
@RequestParam String code
|
||||
) {
|
||||
WxMaLoginResp result = wxMaAuthService.silentLogin(code);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号快捷登录
|
||||
* <p>
|
||||
* 适用场景:企业认证小程序(已开通手机号快捷登录权限)
|
||||
* <p>
|
||||
* 一步完成登录,无需绑定流程,自动创建新用户
|
||||
*/
|
||||
@Operation(summary = "手机号快捷登录", description = "同时使用微信 code 和手机号授权 code 登录,适用于企业认证小程序")
|
||||
@PostMapping("/phone-login")
|
||||
@Log(module = LogModuleEnum.LOGIN, value = ActionTypeEnum.LOGIN)
|
||||
public Result<AuthenticationToken> phoneLogin(@Valid @RequestBody WxMaPhoneLoginReq req) {
|
||||
AuthenticationToken result = wxMaAuthService.phoneLogin(req.getLoginCode(), req.getPhoneCode());
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定手机号
|
||||
* <p>
|
||||
* 适用场景:静默登录后未绑定手机号的用户
|
||||
* <p>
|
||||
* 绑定成功后自动完成登录
|
||||
*/
|
||||
@Operation(summary = "绑定手机号", description = "为静默登录用户绑定手机号,绑定成功后自动登录")
|
||||
@PostMapping("/bind-mobile")
|
||||
@Log(module = LogModuleEnum.LOGIN, value = ActionTypeEnum.LOGIN)
|
||||
public Result<AuthenticationToken> bindMobile(@Valid @RequestBody WxMaBindMobileReq req) {
|
||||
AuthenticationToken result = wxMaAuthService.bindMobile(req.getOpenid(), req.getMobile(), req.getSmsCode());
|
||||
return Result.success(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.youlai.boot.auth.model;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 登录请求参数
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Schema(description = "登录请求参数")
|
||||
@Data
|
||||
public class LoginReq {
|
||||
|
||||
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "admin")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "验证码缓存ID", example = "captcha_id_123")
|
||||
private String captchaId;
|
||||
|
||||
@Schema(description = "验证码", example = "123456")
|
||||
private String captchaCode;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.youlai.boot.auth.model;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 微信小程序绑定手机号请求
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Schema(description = "微信小程序绑定手机号请求")
|
||||
@Data
|
||||
public class WxMaBindMobileReq {
|
||||
|
||||
@NotBlank(message = "openid 不能为空")
|
||||
@Schema(description = "微信用户唯一标识(静默登录返回)", example = "oVBkZ0aYgDMDIywRdgPW8-joxXc4")
|
||||
private String openid;
|
||||
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Schema(description = "手机号码", example = "18888888888")
|
||||
private String mobile;
|
||||
|
||||
@NotBlank(message = "短信验证码不能为空")
|
||||
@Schema(description = "短信验证码", example = "123456")
|
||||
private String smsCode;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.youlai.boot.auth.model;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 微信小程序登录响应
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "微信小程序登录响应")
|
||||
public class WxMaLoginResp {
|
||||
|
||||
@Schema(description = "是否新用户")
|
||||
private Boolean isNewUser;
|
||||
|
||||
@Schema(description = "是否需要绑定手机号")
|
||||
private Boolean needBindMobile;
|
||||
|
||||
@Schema(description = "微信openid(绑定手机号时需要)")
|
||||
private String openid;
|
||||
|
||||
@Schema(description = "访问令牌")
|
||||
private String accessToken;
|
||||
|
||||
@Schema(description = "刷新令牌")
|
||||
private String refreshToken;
|
||||
|
||||
@Schema(description = "令牌类型")
|
||||
private String tokenType;
|
||||
|
||||
@Schema(description = "过期时间(秒)")
|
||||
private Integer expiresIn;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.youlai.boot.auth.model;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 微信小程序手机号快捷登录请求
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Schema(description = "微信小程序手机号快捷登录请求")
|
||||
@Data
|
||||
public class WxMaPhoneLoginReq {
|
||||
|
||||
@NotBlank(message = "微信登录凭证不能为空")
|
||||
@Schema(description = "微信登录凭证(wx.login 获取)", example = "0xxx")
|
||||
private String loginCode;
|
||||
|
||||
@NotBlank(message = "手机号授权凭证不能为空")
|
||||
@Schema(description = "手机号授权凭证(getPhoneNumber 事件获取)", example = "0xxx")
|
||||
private String phoneCode;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.youlai.boot.auth.service;
|
||||
|
||||
import com.youlai.boot.framework.captcha.model.CaptchaInfo;
|
||||
import com.youlai.boot.framework.security.model.AuthenticationToken;
|
||||
|
||||
/**
|
||||
* 认证服务接口
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public interface AuthService {
|
||||
|
||||
/**
|
||||
* 账号密码登录
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param password 密码
|
||||
* @return 认证令牌
|
||||
*/
|
||||
AuthenticationToken login(String username, String password);
|
||||
|
||||
/**
|
||||
* 短信验证码登录
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param code 验证码
|
||||
* @return 认证令牌
|
||||
*/
|
||||
AuthenticationToken loginBySms(String mobile, String code);
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
*/
|
||||
void sendSmsCode(String mobile);
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
void logout();
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
CaptchaInfo getCaptcha();
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*
|
||||
* @param refreshToken 刷新令牌
|
||||
* @return 认证令牌
|
||||
*/
|
||||
AuthenticationToken refreshToken(String refreshToken);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.youlai.boot.auth.service;
|
||||
|
||||
import com.youlai.boot.auth.model.WxMaLoginResp;
|
||||
import com.youlai.boot.framework.security.model.AuthenticationToken;
|
||||
|
||||
/**
|
||||
* 微信小程序认证服务接口
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public interface WxMaAuthService {
|
||||
|
||||
/**
|
||||
* 静默登录
|
||||
* <p>
|
||||
* 通过微信登录凭证(code)获取用户唯一标识(openid),
|
||||
* 如果用户已绑定手机号则直接登录成功,否则返回需绑定手机号的提示。
|
||||
* </p>
|
||||
*
|
||||
* @param code 微信登录凭证(wx.login 获取)
|
||||
* @return 登录结果(成功返回 token,需绑定返回 openid)
|
||||
*/
|
||||
WxMaLoginResp silentLogin(String code);
|
||||
|
||||
/**
|
||||
* 手机号快捷登录
|
||||
* <p>
|
||||
* 同时使用微信登录凭证和手机号授权凭证,
|
||||
* 一步完成用户注册/登录,无需额外绑定流程。
|
||||
* 适用于企业认证的小程序(已开通手机号快捷登录权限)。
|
||||
* </p>
|
||||
*
|
||||
* @param loginCode 微信登录凭证(wx.login 获取)
|
||||
* @param phoneCode 手机号授权凭证(getPhoneNumber 事件获取)
|
||||
* @return 认证令牌
|
||||
*/
|
||||
AuthenticationToken phoneLogin(String loginCode, String phoneCode);
|
||||
|
||||
/**
|
||||
* 绑定手机号
|
||||
* <p>
|
||||
* 为已静默登录但未绑定手机号的用户绑定手机号,
|
||||
* 绑定成功后自动完成登录。
|
||||
* </p>
|
||||
*
|
||||
* @param openid 微信用户唯一标识
|
||||
* @param mobile 手机号码
|
||||
* @param smsCode 短信验证码
|
||||
* @return 认证令牌
|
||||
*/
|
||||
AuthenticationToken bindMobile(String openid, String mobile, String smsCode);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.youlai.boot.auth.service.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.boot.auth.service.AuthService;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.framework.captcha.model.CaptchaInfo;
|
||||
import com.youlai.boot.framework.captcha.service.CaptchaService;
|
||||
import com.youlai.boot.framework.security.model.AuthenticationToken;
|
||||
import com.youlai.boot.framework.security.model.SmsAuthenticationToken;
|
||||
import com.youlai.boot.framework.security.token.TokenManager;
|
||||
import com.youlai.boot.framework.security.util.SecurityUtils;
|
||||
import com.youlai.boot.framework.integration.sms.enums.SmsTypeEnum;
|
||||
import com.youlai.boot.framework.integration.sms.service.SmsService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 认证服务实现类
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AuthServiceImpl implements AuthService {
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private final TokenManager tokenManager;
|
||||
|
||||
private final SmsService smsService;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final CaptchaService captchaService;
|
||||
|
||||
/**
|
||||
* 用户名密码登录
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param password 密码
|
||||
* @return 访问令牌
|
||||
*/
|
||||
@Override
|
||||
public AuthenticationToken login(String username, String password) {
|
||||
// 1. 创建用于密码认证的令牌(未认证)
|
||||
UsernamePasswordAuthenticationToken authenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(username.trim(), password);
|
||||
|
||||
// 2. 执行认证(认证中)
|
||||
// 说明:这里的认证流程由 Spring Security 提供的 AuthenticationManager 执行。
|
||||
// 默认情况下会委托给 DaoAuthenticationProvider:
|
||||
// 1) retrieveUser(...):内部通过 UserDetailsService.loadUserByUsername(...) 获取用户信息(本项目为 SysUserDetailsService 实现)
|
||||
// 2) additionalAuthenticationChecks(...):对比请求密码与用户存储密码(由 PasswordEncoder 完成匹配)
|
||||
// 认证通过后返回已认证的 Authentication(principal 为 SysUserDetails,authorities 为角色/权限集合)。
|
||||
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||
|
||||
// 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证)
|
||||
AuthenticationToken authenticationTokenResponse =
|
||||
tokenManager.generateToken(authentication);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
return authenticationTokenResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送登录短信验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
*/
|
||||
@Override
|
||||
public void sendSmsCode(String mobile) {
|
||||
String code = RandomUtil.randomNumbers(6);
|
||||
|
||||
// 发送短信验证码
|
||||
Map<String, String> templateParams = new HashMap<>();
|
||||
templateParams.put("code", code);
|
||||
try {
|
||||
smsService.sendSms(mobile, SmsTypeEnum.LOGIN, templateParams);
|
||||
} catch (Exception e) {
|
||||
log.error("发送短信验证码失败", e);
|
||||
}
|
||||
// 缓存验证码至Redis,用于登录校验
|
||||
redisTemplate.opsForValue().set(StrUtil.format(RedisConstants.Captcha.SMS_LOGIN_CODE, mobile), code, 5, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信验证码登录
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param code 验证码
|
||||
* @return 访问令牌
|
||||
*/
|
||||
@Override
|
||||
public AuthenticationToken loginBySms(String mobile, String code) {
|
||||
// 1. 创建用户短信验证码认证的令牌(未认证)
|
||||
SmsAuthenticationToken smsAuthenticationToken = new SmsAuthenticationToken(mobile, code);
|
||||
|
||||
// 2. 执行认证(认证中)
|
||||
Authentication authentication = authenticationManager.authenticate(smsAuthenticationToken);
|
||||
|
||||
// 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证)
|
||||
AuthenticationToken authenticationToken = tokenManager.generateToken(authentication);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
return authenticationToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销登录
|
||||
*/
|
||||
@Override
|
||||
public void logout() {
|
||||
String token = SecurityUtils.getAccessToken();
|
||||
if (StrUtil.isNotBlank(token)) {
|
||||
tokenManager.invalidateToken(token);
|
||||
// 清除Security上下文
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
@Override
|
||||
public CaptchaInfo getCaptcha() {
|
||||
return captchaService.generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
*
|
||||
* @param refreshToken 刷新令牌
|
||||
* @return 新的访问令牌
|
||||
*/
|
||||
@Override
|
||||
public AuthenticationToken refreshToken(String refreshToken) {
|
||||
return tokenManager.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package com.youlai.boot.auth.service.impl;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.boot.auth.model.WxMaLoginResp;
|
||||
import com.youlai.boot.auth.service.WxMaAuthService;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.framework.security.exception.NeedBindMobileException;
|
||||
import com.youlai.boot.framework.security.model.AuthenticationToken;
|
||||
import com.youlai.boot.framework.security.model.SysUserDetails;
|
||||
import com.youlai.boot.framework.security.model.WxMaAuthenticationToken;
|
||||
import com.youlai.boot.framework.security.token.TokenManager;
|
||||
import com.youlai.boot.system.enums.SocialPlatformEnum;
|
||||
import com.youlai.boot.system.model.entity.SysUser;
|
||||
import com.youlai.boot.system.service.UserSocialService;
|
||||
import com.youlai.boot.system.service.UserService;
|
||||
import com.youlai.boot.system.service.UserRoleService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 微信小程序认证服务实现
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class WxMaAuthServiceImpl implements WxMaAuthService {
|
||||
|
||||
private final WxMaService wxMaService;
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private final TokenManager tokenManager;
|
||||
private final UserService userService;
|
||||
private final UserSocialService userSocialService;
|
||||
private final UserRoleService userRoleService;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* 静默登录
|
||||
*/
|
||||
@Override
|
||||
public WxMaLoginResp silentLogin(String code) {
|
||||
WxMaAuthenticationToken token = new WxMaAuthenticationToken(code);
|
||||
|
||||
try {
|
||||
Authentication authentication = authenticationManager.authenticate(token);
|
||||
AuthenticationToken authToken = tokenManager.generateToken(authentication);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
return WxMaLoginResp.builder()
|
||||
.isNewUser(false)
|
||||
.needBindMobile(false)
|
||||
.accessToken(authToken.getAccessToken())
|
||||
.refreshToken(authToken.getRefreshToken())
|
||||
.tokenType(authToken.getTokenType())
|
||||
.expiresIn(authToken.getExpiresIn())
|
||||
.build();
|
||||
} catch (NeedBindMobileException e) {
|
||||
return WxMaLoginResp.builder()
|
||||
.isNewUser(true)
|
||||
.needBindMobile(true)
|
||||
.openid(e.getOpenid())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号快捷登录
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AuthenticationToken phoneLogin(String loginCode, String phoneCode) {
|
||||
// 1. 解析微信登录凭证,获取会话信息
|
||||
WxMaJscode2SessionResult session = resolveSession(loginCode);
|
||||
String openid = session.getOpenid();
|
||||
|
||||
// 2. 解析手机号授权凭证,获取手机号
|
||||
String mobile = resolvePhoneNumber(phoneCode);
|
||||
|
||||
log.info("微信小程序手机号快捷登录:openid={}, mobile={}", openid, mobile);
|
||||
|
||||
// 3. 查询或创建用户
|
||||
SysUser user = findOrCreateUser(mobile);
|
||||
|
||||
// 4. 绑定微信 openid
|
||||
bindWechatOpenid(user, session);
|
||||
|
||||
// 5. 生成认证令牌
|
||||
return generateAuthToken(mobile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定手机号
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AuthenticationToken bindMobile(String openid, String mobile, String smsCode) {
|
||||
// 1. 验证短信验证码
|
||||
validateSmsCode(mobile, smsCode);
|
||||
|
||||
// 2. 查询或创建用户
|
||||
SysUser user = findOrCreateUser(mobile);
|
||||
|
||||
// 3. 绑定微信 openid
|
||||
userSocialService.bindOrUpdate(
|
||||
user.getId(),
|
||||
SocialPlatformEnum.WECHAT_MINI,
|
||||
openid,
|
||||
null, null, null, null
|
||||
);
|
||||
|
||||
log.info("微信小程序绑定手机号成功:mobile={}, openid={}", mobile, openid);
|
||||
|
||||
// 4. 生成认证令牌
|
||||
return generateAuthToken(mobile);
|
||||
}
|
||||
|
||||
// ==================== 私有方法 ====================
|
||||
|
||||
/**
|
||||
* 解析微信登录凭证,获取会话信息
|
||||
*/
|
||||
private WxMaJscode2SessionResult resolveSession(String loginCode) {
|
||||
try {
|
||||
return wxMaService.jsCode2SessionInfo(loginCode);
|
||||
} catch (Exception e) {
|
||||
log.error("获取微信会话信息失败,loginCode={}", loginCode, e);
|
||||
throw new IllegalArgumentException("微信登录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析手机号授权凭证,获取手机号
|
||||
*/
|
||||
private String resolvePhoneNumber(String phoneCode) {
|
||||
try {
|
||||
WxMaPhoneNumberInfo phoneInfo = wxMaService.getUserService().getPhoneNoInfo(phoneCode);
|
||||
return phoneInfo.getPhoneNumber();
|
||||
} catch (Exception e) {
|
||||
log.error("获取微信手机号失败,phoneCode={}", phoneCode, e);
|
||||
throw new IllegalArgumentException("获取手机号失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询或创建用户
|
||||
*/
|
||||
private SysUser findOrCreateUser(String mobile) {
|
||||
SysUser user = userService.lambdaQuery()
|
||||
.eq(SysUser::getMobile, mobile)
|
||||
.one();
|
||||
|
||||
if (user == null) {
|
||||
user = createNewUser(mobile);
|
||||
log.info("微信小程序登录:创建新用户,mobile={}, userId={}", mobile, user.getId());
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新用户
|
||||
* <p>
|
||||
* 新用户默认分配 GUEST(访问游客)角色
|
||||
* </p>
|
||||
*/
|
||||
private SysUser createNewUser(String mobile) {
|
||||
SysUser user = new SysUser();
|
||||
user.setMobile(mobile);
|
||||
user.setUsername("wx_" + IdUtil.fastSimpleUUID().substring(0, 8));
|
||||
user.setNickname("微信用户");
|
||||
user.setStatus(1);
|
||||
user.setIsDeleted(0);
|
||||
user.setCreateTime(LocalDateTime.now());
|
||||
user.setUpdateTime(LocalDateTime.now());
|
||||
userService.save(user);
|
||||
|
||||
// 分配 GUEST 角色(角色ID=3)
|
||||
userRoleService.saveUserRoles(user.getId(), Collections.singletonList(3L));
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定微信 openid
|
||||
*/
|
||||
private void bindWechatOpenid(SysUser user, WxMaJscode2SessionResult session) {
|
||||
try {
|
||||
userSocialService.bindOrUpdate(
|
||||
user.getId(),
|
||||
SocialPlatformEnum.WECHAT_MINI,
|
||||
session.getOpenid(),
|
||||
session.getUnionid(),
|
||||
user.getNickname(),
|
||||
user.getAvatar(),
|
||||
session.getSessionKey()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
// 绑定失败不影响登录
|
||||
log.warn("绑定微信 openid 失败,userId={}, openid={}", user.getId(), session.getOpenid(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证短信验证码
|
||||
*/
|
||||
private void validateSmsCode(String mobile, String smsCode) {
|
||||
String cacheKey = StrUtil.format(RedisConstants.Captcha.SMS_LOGIN_CODE, mobile);
|
||||
String cachedCode = (String) redisTemplate.opsForValue().get(cacheKey);
|
||||
|
||||
if (!StrUtil.equals(smsCode, cachedCode)) {
|
||||
throw new IllegalArgumentException("验证码错误");
|
||||
}
|
||||
|
||||
// 验证成功后删除验证码
|
||||
redisTemplate.delete(cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成认证令牌
|
||||
*/
|
||||
private AuthenticationToken generateAuthToken(String mobile) {
|
||||
SysUserDetails userDetails = new SysUserDetails(userService.getAuthInfoByMobile(mobile));
|
||||
Authentication authentication = new UsernamePasswordAuthenticationToken(
|
||||
userDetails, null, userDetails.getAuthorities()
|
||||
);
|
||||
AuthenticationToken authToken = tokenManager.generateToken(authentication);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
return authToken;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.youlai.boot.codegen.config;
|
||||
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 代码生成配置属性
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@Component
|
||||
@EnableConfigurationProperties(CodegenProperties.class)
|
||||
@ConfigurationProperties(prefix = "codegen")
|
||||
@Data
|
||||
public class CodegenProperties {
|
||||
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
private DefaultConfig defaultConfig ;
|
||||
|
||||
/**
|
||||
* 模板配置
|
||||
*/
|
||||
private Map<String, TemplateConfig> templateConfigs = MapUtil.newHashMap(true);
|
||||
|
||||
/**
|
||||
* 后端应用名
|
||||
*/
|
||||
private String backendAppName;
|
||||
|
||||
/**
|
||||
* 前端应用名
|
||||
*/
|
||||
private String frontendAppName;
|
||||
|
||||
/**
|
||||
* 下载文件名
|
||||
*/
|
||||
private String downloadFileName;
|
||||
|
||||
/**
|
||||
* 排除数据表
|
||||
*/
|
||||
private List<String> excludeTables;
|
||||
|
||||
/**
|
||||
* 模板配置
|
||||
*/
|
||||
@Data
|
||||
public static class TemplateConfig {
|
||||
|
||||
/**
|
||||
* 模板路径 (e.g. /templates/codegen/controller.java.vm)
|
||||
*/
|
||||
private String templatePath;
|
||||
|
||||
/**
|
||||
* 子包名 (e.g. controller/service/mapper/model)
|
||||
*/
|
||||
private String subpackageName;
|
||||
|
||||
/**
|
||||
* 文件扩展名,如 .java
|
||||
*/
|
||||
private String extension = FileNameUtil.EXT_JAVA;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
@Data
|
||||
public static class DefaultConfig {
|
||||
|
||||
/**
|
||||
* 作者 (e.g. Ray)
|
||||
*/
|
||||
private String author;
|
||||
|
||||
/**
|
||||
* 默认模块名(e.g. system)
|
||||
*/
|
||||
private String moduleName;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.youlai.boot.codegen.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.boot.common.result.PageResult;
|
||||
import com.youlai.boot.common.result.Result;
|
||||
import com.youlai.boot.codegen.config.CodegenProperties;
|
||||
import com.youlai.boot.common.enums.ActionTypeEnum;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
import com.youlai.boot.codegen.service.CodegenService;
|
||||
import com.youlai.boot.codegen.model.form.GenConfigForm;
|
||||
import com.youlai.boot.codegen.model.query.TableQuery;
|
||||
import com.youlai.boot.codegen.model.vo.CodegenPreviewVO;
|
||||
import com.youlai.boot.codegen.model.vo.TablePageVO;
|
||||
import com.youlai.boot.common.annotation.Log;
|
||||
import com.youlai.boot.codegen.service.GenTableService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成器控制层
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Tag(name = "11.代码生成")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/codegen")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class CodegenController {
|
||||
|
||||
private final CodegenService codegenService;
|
||||
private final GenTableService genTableService;
|
||||
private final CodegenProperties codegenProperties;
|
||||
|
||||
@Operation(summary = "获取数据表分页列表")
|
||||
@GetMapping("/table")
|
||||
public PageResult<TablePageVO> getTablePage(
|
||||
TableQuery queryParams
|
||||
) {
|
||||
Page<TablePageVO> result = codegenService.getTablePage(queryParams);
|
||||
return PageResult.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取代码生成配置")
|
||||
@GetMapping("/{tableName}/config")
|
||||
public Result<GenConfigForm> getGenTableFormData(
|
||||
@Parameter(description = "表名", example = "sys_user") @PathVariable String tableName
|
||||
) {
|
||||
GenConfigForm formData = genTableService.getGenTableFormData(tableName);
|
||||
return Result.success(formData);
|
||||
}
|
||||
|
||||
@Operation(summary = "保存代码生成配置")
|
||||
@PostMapping("/{tableName}/config")
|
||||
@Log(module = LogModuleEnum.CODEGEN, value = ActionTypeEnum.UPDATE)
|
||||
public Result<?> saveGenConfig(@RequestBody GenConfigForm formData) {
|
||||
genTableService.saveGenConfig(formData);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "删除代码生成配置")
|
||||
@DeleteMapping("/{tableName}/config")
|
||||
public Result<?> deleteGenConfig(
|
||||
@Parameter(description = "表名", example = "sys_user") @PathVariable String tableName
|
||||
) {
|
||||
genTableService.deleteGenConfig(tableName);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取预览生成代码")
|
||||
@GetMapping("/{tableName}/preview")
|
||||
public Result<List<CodegenPreviewVO>> getTablePreviewData(@PathVariable String tableName,
|
||||
@RequestParam(value = "pageType", required = false, defaultValue = "classic") String pageType,
|
||||
@RequestParam(value = "type", required = false, defaultValue = "ts") String type) {
|
||||
List<CodegenPreviewVO> list = codegenService.getCodegenPreviewData(tableName, pageType, type);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
@Operation(summary = "下载代码")
|
||||
@GetMapping("/{tableName}/download")
|
||||
@Log(module = LogModuleEnum.CODEGEN, value = ActionTypeEnum.DOWNLOAD)
|
||||
public void downloadZip(HttpServletResponse response, @PathVariable String tableName,
|
||||
@RequestParam(value = "pageType", required = false, defaultValue = "classic") String pageType,
|
||||
@RequestParam(value = "type", required = false, defaultValue = "ts") String type) {
|
||||
String[] tableNames = tableName.split(",");
|
||||
byte[] data = codegenService.downloadCode(tableNames, pageType, type);
|
||||
|
||||
response.reset();
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(codegenProperties.getDownloadFileName(), StandardCharsets.UTF_8));
|
||||
response.setContentType("application/octet-stream; charset=UTF-8");
|
||||
|
||||
try (ServletOutputStream outputStream = response.getOutputStream()) {
|
||||
outputStream.write(data);
|
||||
outputStream.flush();
|
||||
} catch (IOException e) {
|
||||
log.error("Error while writing the zip file1 to response", e);
|
||||
throw new RuntimeException("Failed to write the zip file1 to response", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.youlai.boot.codegen.converter;
|
||||
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import com.youlai.boot.codegen.model.form.GenConfigForm;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成配置转换器
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface CodegenConverter {
|
||||
|
||||
@Mapping(source = "genTable.tableName", target = "tableName")
|
||||
@Mapping(source = "genTable.businessName", target = "businessName")
|
||||
@Mapping(source = "genTable.moduleName", target = "moduleName")
|
||||
@Mapping(source = "genTable.packageName", target = "packageName")
|
||||
@Mapping(source = "genTable.entityName", target = "entityName")
|
||||
@Mapping(source = "genTable.author", target = "author")
|
||||
@Mapping(source = "genTable.pageType", target = "pageType")
|
||||
@Mapping(source = "genTable.removeTablePrefix", target = "removeTablePrefix")
|
||||
@Mapping(source = "fieldConfigs", target = "fieldConfigs")
|
||||
GenConfigForm toGenConfigForm(GenTable genTable, List<GenTableColumn> fieldConfigs);
|
||||
|
||||
List<GenConfigForm.FieldConfig> toGenTableColumnForm(List<GenTableColumn> fieldConfigs);
|
||||
|
||||
GenConfigForm.FieldConfig toGenTableColumnForm(GenTableColumn genTableColumn);
|
||||
|
||||
GenTable toGenTable(GenConfigForm formData);
|
||||
|
||||
List<GenTableColumn> toGenTableColumn(List<GenConfigForm.FieldConfig> fieldConfigs);
|
||||
|
||||
GenTableColumn toGenTableColumn(GenConfigForm.FieldConfig fieldConfig);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.youlai.boot.codegen.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.youlai.boot.common.base.IBaseEnum;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 表单类型枚举
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum FormTypeEnum implements IBaseEnum<Integer> {
|
||||
|
||||
/**
|
||||
* 输入框
|
||||
*/
|
||||
INPUT(1, "输入框"),
|
||||
|
||||
/**
|
||||
* 下拉框
|
||||
*/
|
||||
SELECT(2, "下拉框"),
|
||||
|
||||
/**
|
||||
* 单选框
|
||||
*/
|
||||
RADIO(3, "单选框"),
|
||||
|
||||
/**
|
||||
* 复选框
|
||||
*/
|
||||
CHECK_BOX(4, "复选框"),
|
||||
|
||||
/**
|
||||
* 数字输入框
|
||||
*/
|
||||
INPUT_NUMBER(5, "数字输入框"),
|
||||
|
||||
/**
|
||||
* 开关
|
||||
*/
|
||||
SWITCH(6, "开关"),
|
||||
|
||||
/**
|
||||
* 文本域
|
||||
*/
|
||||
TEXT_AREA(7, "文本域"),
|
||||
|
||||
/**
|
||||
* 日期时间框
|
||||
*/
|
||||
DATE(8, "日期框"),
|
||||
|
||||
/**
|
||||
* 日期框
|
||||
*/
|
||||
DATE_TIME(9, "日期时间框"),
|
||||
|
||||
/**
|
||||
* 隐藏域
|
||||
*/
|
||||
HIDDEN(10, "隐藏域");
|
||||
|
||||
|
||||
// Mybatis-Plus 提供注解表示插入数据库时插入该值
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final Integer value;
|
||||
|
||||
// @JsonValue // 表示对枚举序列化时返回此字段
|
||||
private final String label;
|
||||
|
||||
|
||||
@JsonCreator
|
||||
public static FormTypeEnum fromValue(Integer value) {
|
||||
for (FormTypeEnum type : FormTypeEnum.values()) {
|
||||
if (type.getValue().equals(value)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No enum constant with value " + value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.youlai.boot.codegen.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表单类型枚举
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Getter
|
||||
public enum JavaTypeEnum {
|
||||
|
||||
VARCHAR("varchar", "String", "string"),
|
||||
CHAR("char", "String", "string"),
|
||||
BLOB("blob", "byte[]", "Uint8Array"),
|
||||
TEXT("text", "String", "string"),
|
||||
JSON("json", "String", "any"),
|
||||
INTEGER("int", "Integer", "number"),
|
||||
TINYINT("tinyint", "Integer", "number"),
|
||||
SMALLINT("smallint", "Integer", "number"),
|
||||
MEDIUMINT("mediumint", "Integer", "number"),
|
||||
BIGINT("bigint", "Long", "number"),
|
||||
FLOAT("float", "Float", "number"),
|
||||
DOUBLE("double", "Double", "number"),
|
||||
DECIMAL("decimal", "BigDecimal", "number"),
|
||||
DATE("date", "LocalDate", "string"),
|
||||
DATETIME("datetime", "LocalDateTime", "string"),
|
||||
TIMESTAMP("timestamp", "LocalDateTime", "string"),
|
||||
BOOLEAN("boolean", "Boolean", "boolean"),
|
||||
BIT("bit", "Boolean", "boolean");
|
||||
|
||||
// 数据库类型
|
||||
private final String dbType;
|
||||
// Java类型
|
||||
private final String javaType;
|
||||
// TypeScript类型
|
||||
private final String tsType;
|
||||
|
||||
// 数据库类型和Java类型的映射
|
||||
private static final Map<String, JavaTypeEnum> typeMap = new HashMap<>();
|
||||
|
||||
// 初始化映射关系
|
||||
static {
|
||||
for (JavaTypeEnum javaTypeEnum : JavaTypeEnum.values()) {
|
||||
typeMap.put(javaTypeEnum.getDbType(), javaTypeEnum);
|
||||
}
|
||||
}
|
||||
|
||||
JavaTypeEnum(String dbType, String javaType, String tsType) {
|
||||
this.dbType = dbType;
|
||||
this.javaType = javaType;
|
||||
this.tsType = tsType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数据库类型获取对应的Java类型
|
||||
*
|
||||
* @param columnType 列类型
|
||||
* @return 对应的Java类型
|
||||
*/
|
||||
public static String getJavaTypeByColumnType(String columnType) {
|
||||
String normalized = normalizeColumnType(columnType);
|
||||
JavaTypeEnum javaTypeEnum = typeMap.get(normalized);
|
||||
if (javaTypeEnum != null) {
|
||||
return javaTypeEnum.getJavaType();
|
||||
}
|
||||
return "String";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Java类型获取对应的TypeScript类型
|
||||
*
|
||||
* @param javaType Java类型
|
||||
* @return 对应的TypeScript类型
|
||||
*/
|
||||
public static String getTsTypeByJavaType(String javaType) {
|
||||
if (javaType == null) {
|
||||
return "any";
|
||||
}
|
||||
for (JavaTypeEnum javaTypeEnum : JavaTypeEnum.values()) {
|
||||
if (javaTypeEnum.getJavaType().equals(javaType)) {
|
||||
return javaTypeEnum.getTsType();
|
||||
}
|
||||
}
|
||||
return "any";
|
||||
}
|
||||
|
||||
private static String normalizeColumnType(String columnType) {
|
||||
if (columnType == null) {
|
||||
return "";
|
||||
}
|
||||
// Handle values like: varchar(255), bigint unsigned, INT
|
||||
String normalized = columnType.trim().toLowerCase();
|
||||
int parenIndex = normalized.indexOf('(');
|
||||
if (parenIndex > -1) {
|
||||
normalized = normalized.substring(0, parenIndex);
|
||||
}
|
||||
// Remove modifiers
|
||||
normalized = normalized.replace("unsigned", "").replace("zerofill", "").trim();
|
||||
// Collapse repeated spaces
|
||||
normalized = normalized.replaceAll("\\s+", " ");
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.youlai.boot.codegen.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.youlai.boot.common.base.IBaseEnum;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 查询类型枚举
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum QueryTypeEnum implements IBaseEnum<Integer> {
|
||||
|
||||
/** 等于 */
|
||||
EQ(1, "="),
|
||||
|
||||
/** 模糊匹配 */
|
||||
LIKE(2, "LIKE '%s%'"),
|
||||
|
||||
/** 包含 */
|
||||
IN(3, "IN"),
|
||||
|
||||
/** 范围 */
|
||||
BETWEEN(4, "BETWEEN"),
|
||||
|
||||
/** 大于 */
|
||||
GT(5, ">"),
|
||||
|
||||
/** 大于等于 */
|
||||
GE(6, ">="),
|
||||
|
||||
/** 小于 */
|
||||
LT(7, "<"),
|
||||
|
||||
/** 小于等于 */
|
||||
LE(8, "<="),
|
||||
|
||||
/** 不等于 */
|
||||
NE(9, "!="),
|
||||
|
||||
/** 左模糊匹配 */
|
||||
LIKE_LEFT(10, "LIKE '%s'"),
|
||||
|
||||
/** 右模糊匹配 */
|
||||
LIKE_RIGHT(11, "LIKE 's%'");
|
||||
|
||||
|
||||
// 存储在数据库中的枚举属性值
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final Integer value;
|
||||
|
||||
// 序列化成 JSON 时的属性值
|
||||
private final String label;
|
||||
|
||||
|
||||
@JsonCreator
|
||||
public static QueryTypeEnum fromValue(Integer value) {
|
||||
for (QueryTypeEnum type : QueryTypeEnum.values()) {
|
||||
if (type.getValue().equals(value)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No enum constant with value " + value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.youlai.boot.codegen.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.boot.codegen.model.vo.ColumnMetaVO;
|
||||
import com.youlai.boot.codegen.model.vo.TableMetaVO;
|
||||
import com.youlai.boot.codegen.model.query.TableQuery;
|
||||
import com.youlai.boot.codegen.model.vo.TablePageVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 数据库映射层
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.9.0
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabaseMapper extends BaseMapper {
|
||||
|
||||
/**
|
||||
* 获取表分页列表
|
||||
*
|
||||
* @param page
|
||||
* @param queryParams
|
||||
* @return
|
||||
*/
|
||||
Page<TablePageVO> getTablePage(Page<TablePageVO> page, TableQuery queryParams);
|
||||
|
||||
/**
|
||||
* 获取表字段列表
|
||||
*
|
||||
* @param tableName
|
||||
* @return
|
||||
*/
|
||||
List<ColumnMetaVO> getTableColumns(String tableName);
|
||||
|
||||
TableMetaVO getTableMetadata(String tableName);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.youlai.boot.codegen.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 代码生成表字段配置访问层
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Mapper
|
||||
public interface GenTableColumnMapper extends BaseMapper<GenTableColumn> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.youlai.boot.codegen.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 代码生成表配置访问层
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Mapper
|
||||
public interface GenTableMapper extends BaseMapper<GenTable> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.youlai.boot.codegen.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
|
||||
import com.youlai.boot.common.base.BaseEntity;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 代码生成表配置
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@TableName(value = "gen_table")
|
||||
@Getter
|
||||
@Setter
|
||||
public class GenTable extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 包名
|
||||
*/
|
||||
private String packageName;
|
||||
|
||||
/**
|
||||
* 模块名
|
||||
*/
|
||||
private String moduleName;
|
||||
|
||||
/**
|
||||
* 实体类名
|
||||
*/
|
||||
private String entityName;
|
||||
|
||||
/**
|
||||
* 业务名
|
||||
*/
|
||||
private String businessName;
|
||||
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
private Long parentMenuId;
|
||||
|
||||
/**
|
||||
* 作者
|
||||
*/
|
||||
private String author;
|
||||
|
||||
/**
|
||||
* 页面类型 classic|curd
|
||||
*/
|
||||
private String pageType;
|
||||
|
||||
/**
|
||||
* 要移除的表前缀,如: sys_
|
||||
*/
|
||||
private String removeTablePrefix;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.youlai.boot.codegen.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.youlai.boot.common.base.BaseEntity;
|
||||
import com.youlai.boot.codegen.enums.FormTypeEnum;
|
||||
import com.youlai.boot.codegen.enums.QueryTypeEnum;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 代码生成表字段配置实体
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@TableName(value = "gen_table_column")
|
||||
@Getter
|
||||
@Setter
|
||||
public class GenTableColumn extends BaseEntity {
|
||||
|
||||
|
||||
/**
|
||||
* 关联的表配置ID
|
||||
*/
|
||||
private Long tableId;
|
||||
|
||||
/**
|
||||
* 列名
|
||||
*/
|
||||
private String columnName;
|
||||
|
||||
/**
|
||||
* 列类型
|
||||
*/
|
||||
private String columnType;
|
||||
|
||||
/**
|
||||
* 字段长度
|
||||
*/
|
||||
private Long maxLength;
|
||||
|
||||
/**
|
||||
* 字段名称
|
||||
*/
|
||||
private String fieldName;
|
||||
|
||||
/**
|
||||
* 字段排序
|
||||
*/
|
||||
private Integer fieldSort;
|
||||
|
||||
/**
|
||||
* 字段类型
|
||||
*/
|
||||
private String fieldType;
|
||||
|
||||
/**
|
||||
* 字段描述
|
||||
*/
|
||||
private String fieldComment;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*/
|
||||
private FormTypeEnum formType;
|
||||
|
||||
/**
|
||||
* 查询方式
|
||||
*/
|
||||
private QueryTypeEnum queryType;
|
||||
|
||||
/**
|
||||
* 是否在列表显示
|
||||
*/
|
||||
private Integer isShowInList;
|
||||
|
||||
/**
|
||||
* 是否在表单显示
|
||||
*/
|
||||
private Integer isShowInForm;
|
||||
|
||||
/**
|
||||
* 是否在查询条件显示
|
||||
*/
|
||||
private Integer isShowInQuery;
|
||||
|
||||
/**
|
||||
* 是否必填
|
||||
*/
|
||||
private Integer isRequired;
|
||||
|
||||
/**
|
||||
* TypeScript类型
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
@JsonIgnore
|
||||
private String tsType;
|
||||
|
||||
/**
|
||||
* 字典类型
|
||||
*/
|
||||
private String dictType;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.youlai.boot.codegen.model.form;
|
||||
|
||||
import com.youlai.boot.codegen.enums.FormTypeEnum;
|
||||
import com.youlai.boot.codegen.enums.QueryTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成配置表单
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Schema(description = "代码生成配置表单")
|
||||
@Data
|
||||
public class GenConfigForm {
|
||||
|
||||
@Schema(description = "主键",example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "表名",example = "sys_user")
|
||||
private String tableName;
|
||||
|
||||
@Schema(description = "业务名",example = "用户")
|
||||
private String businessName;
|
||||
|
||||
@Schema(description = "模块名",example = "system")
|
||||
private String moduleName;
|
||||
|
||||
@Schema(description = "包名",example = "com.youlai")
|
||||
private String packageName;
|
||||
|
||||
@Schema(description = "实体名",example = "User")
|
||||
private String entityName;
|
||||
|
||||
@Schema(description = "作者",example = "youlaitech")
|
||||
private String author;
|
||||
|
||||
@Schema(description = "上级菜单ID",example = "1")
|
||||
private Long parentMenuId;
|
||||
|
||||
@Schema(description = "字段配置列表")
|
||||
private List<FieldConfig> fieldConfigs;
|
||||
|
||||
@Schema(description = "后端应用名")
|
||||
private String backendAppName;
|
||||
|
||||
@Schema(description = "前端应用名")
|
||||
private String frontendAppName;
|
||||
|
||||
@Schema(description = "页面类型 classic|curd", example = "classic")
|
||||
private String pageType;
|
||||
|
||||
@Schema(description = "要移除的表前缀,如: sys_", example = "sys_")
|
||||
private String removeTablePrefix;
|
||||
|
||||
@Schema(description = "字段配置")
|
||||
@Data
|
||||
public static class FieldConfig {
|
||||
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "列名")
|
||||
private String columnName;
|
||||
|
||||
@Schema(description = "列类型")
|
||||
private String columnType;
|
||||
|
||||
@Schema(description = "字段名")
|
||||
private String fieldName;
|
||||
|
||||
@Schema(description = "字段排序")
|
||||
private Integer fieldSort;
|
||||
|
||||
@Schema(description = "字段类型")
|
||||
private String fieldType;
|
||||
|
||||
@Schema(description = "字段描述")
|
||||
private String fieldComment;
|
||||
|
||||
@Schema(description = "是否在列表显示")
|
||||
private Integer isShowInList;
|
||||
|
||||
@Schema(description = "是否在表单显示")
|
||||
private Integer isShowInForm;
|
||||
|
||||
@Schema(description = "是否在查询条件显示")
|
||||
private Integer isShowInQuery;
|
||||
|
||||
@Schema(description = "是否必填")
|
||||
private Integer isRequired;
|
||||
|
||||
@Schema(description = "最大长度")
|
||||
private Integer maxLength;
|
||||
|
||||
@Schema(description = "表单类型")
|
||||
private FormTypeEnum formType;
|
||||
|
||||
@Schema(description = "查询类型")
|
||||
private QueryTypeEnum queryType;
|
||||
|
||||
@Schema(description = "字典类型")
|
||||
private String dictType;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.youlai.boot.codegen.model.query;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.youlai.boot.common.base.BaseQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据表分页查询对象
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Schema(description = "数据表分页查询对象")
|
||||
@Getter
|
||||
@Setter
|
||||
public class TablePageQuery extends BaseQuery {
|
||||
|
||||
@Schema(description="关键字(表名)")
|
||||
private String keywords;
|
||||
|
||||
/**
|
||||
* 排除的表名
|
||||
*/
|
||||
@JsonIgnore
|
||||
private List<String> excludeTables;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.youlai.boot.codegen.model.query;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.youlai.boot.common.base.BaseQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据表查询对象
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Schema(description = "数据表查询对象")
|
||||
@Getter
|
||||
@Setter
|
||||
public class TableQuery extends BaseQuery {
|
||||
|
||||
@Schema(description="关键字(表名)")
|
||||
private String keywords;
|
||||
|
||||
/**
|
||||
* 排除的表名
|
||||
*/
|
||||
@JsonIgnore
|
||||
private List<String> excludeTables;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.youlai.boot.codegen.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "代码生成代码预览Vo")
|
||||
@Data
|
||||
public class CodegenPreviewVO {
|
||||
|
||||
@Schema(description = "生成文件路径")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "生成文件名称",example = "SysUser.java" )
|
||||
private String fileName;
|
||||
|
||||
@Schema(description = "生成文件内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "文件范围(frontend/backend)")
|
||||
private String scope;
|
||||
|
||||
@Schema(description = "文件语言(扩展名)")
|
||||
private String language;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.youlai.boot.codegen.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "数据表字段元数据")
|
||||
@Data
|
||||
public class ColumnMetaVO {
|
||||
|
||||
private String columnName;
|
||||
|
||||
private String dataType;
|
||||
|
||||
private String columnComment;
|
||||
|
||||
private Long characterMaximumLength;
|
||||
|
||||
private Integer isPrimaryKey;
|
||||
|
||||
private String isNullable;
|
||||
|
||||
private String characterSetName;
|
||||
|
||||
private String collationName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.youlai.boot.codegen.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 数据表元数据
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Data
|
||||
public class TableMetaVO {
|
||||
|
||||
private String tableName;
|
||||
|
||||
private String tableComment;
|
||||
|
||||
private String tableCollation;
|
||||
|
||||
private String engine;
|
||||
|
||||
private String charset;
|
||||
|
||||
private String createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.youlai.boot.codegen.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Schema(description = "表视图对象")
|
||||
@Data
|
||||
public class TablePageVO {
|
||||
|
||||
@Schema(description = "表名称", example = "sys_user")
|
||||
private String tableName;
|
||||
|
||||
@Schema(description = "表描述",example = "用户表")
|
||||
private String tableComment;
|
||||
|
||||
@Schema(description = "表排序规则",example = "utf8mb4_general_ci")
|
||||
private String tableCollation;
|
||||
|
||||
@Schema(description = "存储引擎",example = "InnoDB")
|
||||
private String engine;
|
||||
|
||||
@Schema(description = "字符集",example = "utf8mb4")
|
||||
private String charset;
|
||||
|
||||
@Schema(description = "创建时间",example = "2023-08-08 08:08:08")
|
||||
private String createTime;
|
||||
|
||||
@Schema(description="是否已配置")
|
||||
private Integer isConfigured;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.youlai.boot.codegen.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.boot.codegen.model.query.TableQuery;
|
||||
import com.youlai.boot.codegen.model.vo.CodegenPreviewVO;
|
||||
import com.youlai.boot.codegen.model.vo.TablePageVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成配置接口
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public interface CodegenService {
|
||||
|
||||
/**
|
||||
* 获取数据表分页列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
* @return
|
||||
*/
|
||||
Page<TablePageVO> getTablePage(TableQuery queryParams);
|
||||
|
||||
/**
|
||||
* 获取预览生成代码
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return
|
||||
*/
|
||||
List<CodegenPreviewVO> getCodegenPreviewData(String tableName, String pageType, String type);
|
||||
|
||||
/**
|
||||
* 下载代码
|
||||
* @param tableNames 表名
|
||||
* @return
|
||||
*/
|
||||
byte[] downloadCode(String[] tableNames, String pageType, String type);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.youlai.boot.codegen.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
|
||||
/**
|
||||
* 代码生成配置接口
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public interface GenTableColumnService extends IService<GenTableColumn> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.youlai.boot.codegen.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import com.youlai.boot.codegen.model.form.GenConfigForm;
|
||||
|
||||
/**
|
||||
* 代码生成配置接口
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public interface GenTableService extends IService<GenTable> {
|
||||
|
||||
/**
|
||||
* 获取代码生成配置
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return
|
||||
*/
|
||||
GenConfigForm getGenTableFormData(String tableName);
|
||||
|
||||
/**
|
||||
* 保存代码生成配置
|
||||
*
|
||||
* @param formData 表单数据
|
||||
* @return
|
||||
*/
|
||||
void saveGenConfig(GenConfigForm formData);
|
||||
|
||||
/**
|
||||
* 删除代码生成配置
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return
|
||||
*/
|
||||
void deleteGenConfig(String tableName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
package com.youlai.boot.codegen.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.template.Template;
|
||||
import cn.hutool.extra.template.TemplateConfig;
|
||||
import cn.hutool.extra.template.TemplateEngine;
|
||||
import cn.hutool.extra.template.TemplateUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.boot.codegen.enums.JavaTypeEnum;
|
||||
import com.youlai.boot.codegen.config.CodegenProperties;
|
||||
import com.youlai.boot.codegen.service.GenTableService;
|
||||
import com.youlai.boot.codegen.service.GenTableColumnService;
|
||||
import com.youlai.boot.codegen.service.CodegenService;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.codegen.mapper.DatabaseMapper;
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import com.youlai.boot.codegen.model.query.TableQuery;
|
||||
import com.youlai.boot.codegen.model.vo.CodegenPreviewVO;
|
||||
import com.youlai.boot.codegen.model.vo.TablePageVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* 代码生成服务实现类。
|
||||
*
|
||||
* <p>
|
||||
* 根据代码生成配置({@link CodegenProperties})与表/字段元数据,渲染模板并提供预览与下载能力。
|
||||
* </p>
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class CodegenServiceImpl implements CodegenService {
|
||||
|
||||
private final DatabaseMapper databaseMapper;
|
||||
private final CodegenProperties codegenProperties;
|
||||
private final GenTableService genTableService;
|
||||
private final GenTableColumnService genTableColumnService;
|
||||
|
||||
/**
|
||||
* 数据表分页列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
public Page<TablePageVO> getTablePage(TableQuery queryParams) {
|
||||
Page<TablePageVO> page = new Page<>(queryParams.getPageNum(), queryParams.getPageSize());
|
||||
// 设置排除的表
|
||||
List<String> excludeTables = codegenProperties.getExcludeTables();
|
||||
queryParams.setExcludeTables(excludeTables);
|
||||
|
||||
return databaseMapper.getTablePage(page, queryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析前端模板路径
|
||||
*
|
||||
* @param templateName 模板标识
|
||||
* @param templateConfig 模板配置
|
||||
* @param frontendType 前端类型
|
||||
* @return 模板路径
|
||||
*/
|
||||
private String resolveFrontendTemplatePath(String templateName,
|
||||
CodegenProperties.TemplateConfig templateConfig,
|
||||
String frontendType) {
|
||||
if (!"js".equals(frontendType)) {
|
||||
return templateConfig.getTemplatePath();
|
||||
}
|
||||
if ("API".equals(templateName)) {
|
||||
return "codegen/frontend/js/api.js.vm";
|
||||
}
|
||||
if ("VIEW".equals(templateName)) {
|
||||
return "codegen/frontend/js/index.js.vue.vm";
|
||||
}
|
||||
return templateConfig.getTemplatePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析前端文件后缀
|
||||
*
|
||||
* @param templateName 模板标识
|
||||
* @param templateConfig 模板配置
|
||||
* @param frontendType 前端类型
|
||||
* @return 文件后缀
|
||||
*/
|
||||
private String resolveFrontendExtension(String templateName,
|
||||
CodegenProperties.TemplateConfig templateConfig,
|
||||
String frontendType) {
|
||||
if (!"js".equals(frontendType)) {
|
||||
return templateConfig.getExtension();
|
||||
}
|
||||
if ("API".equals(templateName) || "API_TYPES".equals(templateName)) {
|
||||
return ".js";
|
||||
}
|
||||
return templateConfig.getExtension();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预览生成代码
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return 预览数据
|
||||
*/
|
||||
@Override
|
||||
public List<CodegenPreviewVO> getCodegenPreviewData(String tableName, String pageType, String type) {
|
||||
|
||||
List<CodegenPreviewVO> list = new ArrayList<>();
|
||||
|
||||
GenTable genTable = genTableService.getOne(new LambdaQueryWrapper<GenTable>()
|
||||
.eq(GenTable::getTableName, tableName)
|
||||
);
|
||||
if (genTable == null) {
|
||||
throw new BusinessException("未找到表生成配置");
|
||||
}
|
||||
|
||||
List<GenTableColumn> fieldConfigs = genTableColumnService.list(new LambdaQueryWrapper<GenTableColumn>()
|
||||
.eq(GenTableColumn::getTableId, genTable.getId())
|
||||
.orderByAsc(GenTableColumn::getFieldSort)
|
||||
|
||||
);
|
||||
if (CollectionUtil.isEmpty(fieldConfigs)) {
|
||||
throw new BusinessException("未找到字段生成配置");
|
||||
}
|
||||
|
||||
// 遍历模板配置
|
||||
Map<String, CodegenProperties.TemplateConfig> templateConfigs = codegenProperties.getTemplateConfigs();
|
||||
String frontendType = StrUtil.blankToDefault(type, "ts").toLowerCase();
|
||||
for (Map.Entry<String, CodegenProperties.TemplateConfig> templateConfigEntry : templateConfigs.entrySet()) {
|
||||
CodegenPreviewVO previewVo = new CodegenPreviewVO();
|
||||
|
||||
CodegenProperties.TemplateConfig templateConfig = templateConfigEntry.getValue();
|
||||
|
||||
String templateName = templateConfigEntry.getKey();
|
||||
if ("js".equals(frontendType) && "API_TYPES".equals(templateName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String effectiveTemplatePath = resolveFrontendTemplatePath(templateName, templateConfig, frontendType);
|
||||
String extension = resolveFrontendExtension(templateName, templateConfig, frontendType);
|
||||
|
||||
/* 1. 生成文件名 UserController */
|
||||
// User Role Menu Dept
|
||||
String entityName = genTable.getEntityName();
|
||||
// Controller Service Mapper Entity
|
||||
// .java .ts .vue
|
||||
|
||||
// 文件名 UserController.java
|
||||
String fileName = getFileName(entityName, templateName, extension);
|
||||
previewVo.setFileName(fileName);
|
||||
previewVo.setScope(resolveScope(templateName));
|
||||
previewVo.setLanguage(resolveLanguage(fileName));
|
||||
|
||||
/* 2. 生成文件路径 */
|
||||
// 包名:com.youlai.boot
|
||||
String packageName = genTable.getPackageName();
|
||||
// 模块名:system
|
||||
String moduleName = genTable.getModuleName();
|
||||
// 子包名:controller
|
||||
String subpackageName = templateConfig.getSubpackageName();
|
||||
// 组合成文件路径:src/main/java/com/youlai/boot/system/controller
|
||||
String filePath = getFilePath(templateName, moduleName, packageName, subpackageName, entityName);
|
||||
previewVo.setPath(filePath);
|
||||
|
||||
/* 3. 生成文件内容 */
|
||||
// 将模板文件中的变量替换为具体的值 生成代码内容
|
||||
// 优先使用保存的 ui,没有则使用请求参数
|
||||
String finalType = StrUtil.blankToDefault(genTable.getPageType(), pageType);
|
||||
String content = getCodeContent(
|
||||
effectiveTemplatePath,
|
||||
templateConfig.getSubpackageName(),
|
||||
genTable,
|
||||
fieldConfigs,
|
||||
finalType
|
||||
);
|
||||
previewVo.setContent(content);
|
||||
|
||||
list.add(previewVo);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private String resolveScope(String templateName) {
|
||||
return switch (templateName) {
|
||||
case "API", "API_TYPES", "VIEW" -> "frontend";
|
||||
default -> "backend";
|
||||
};
|
||||
}
|
||||
|
||||
private String resolveLanguage(String fileName) {
|
||||
return FileNameUtil.extName(fileName).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件名。
|
||||
*
|
||||
* <p>部分模板需要使用约定的命名规则(例如前端 API 文件)。</p>
|
||||
*
|
||||
* @param entityName 实体名(例如 User)
|
||||
* @param templateName 模板名(例如 Entity、Controller、API)
|
||||
* @param extension 文件后缀(例如 .java、.ts)
|
||||
* @return 文件名
|
||||
*/
|
||||
private String getFileName(String entityName, String templateName, String extension) {
|
||||
if ("Entity".equals(templateName)) {
|
||||
return entityName + extension;
|
||||
} else if ("MapperXml".equals(templateName)) {
|
||||
return entityName + "Mapper" + extension;
|
||||
} else if ("API".equals(templateName)) {
|
||||
return "index" + extension;
|
||||
} else if ("API_TYPES".equals(templateName)) {
|
||||
return "types" + extension;
|
||||
} else if ("VIEW".equals(templateName)) {
|
||||
return "index.vue";
|
||||
}
|
||||
return entityName + templateName + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件路径。
|
||||
*
|
||||
* @param templateName 模板名
|
||||
* @param moduleName 模块名(例如 system)
|
||||
* @param packageName 包名(例如 com.youlai.boot)
|
||||
* @param subPackageName 子包名(例如 controller、service.impl、api、views)
|
||||
* @param entityName 实体名(例如 User)
|
||||
* @return 生成文件路径
|
||||
*/
|
||||
private String getFilePath(String templateName, String moduleName, String packageName, String subPackageName, String entityName) {
|
||||
String path;
|
||||
if ("MapperXml".equals(templateName)) {
|
||||
path = (codegenProperties.getBackendAppName()
|
||||
+ File.separator
|
||||
+ "src" + File.separator + "main" + File.separator + "resources"
|
||||
+ File.separator + subPackageName
|
||||
+ File.separator + moduleName
|
||||
);
|
||||
} else if ("API".equals(templateName)) {
|
||||
path = (codegenProperties.getFrontendAppName()
|
||||
+ File.separator + "src"
|
||||
+ File.separator + "api"
|
||||
+ File.separator + moduleName
|
||||
+ File.separator + StrUtil.toSymbolCase(entityName, '-')
|
||||
);
|
||||
} else if ("API_TYPES".equals(templateName)) {
|
||||
path = (codegenProperties.getFrontendAppName()
|
||||
+ File.separator + "src"
|
||||
+ File.separator + "api"
|
||||
+ File.separator + moduleName
|
||||
+ File.separator + StrUtil.toSymbolCase(entityName, '-')
|
||||
);
|
||||
} else if ("VIEW".equals(templateName)) {
|
||||
// path = "src/views/system/user";
|
||||
path = (codegenProperties.getFrontendAppName()
|
||||
+ File.separator + "src"
|
||||
+ File.separator + subPackageName
|
||||
+ File.separator + moduleName
|
||||
+ File.separator + StrUtil.toSymbolCase(entityName, '-')
|
||||
);
|
||||
} else {
|
||||
path = (codegenProperties.getBackendAppName()
|
||||
+ File.separator
|
||||
+ "src" + File.separator + "main" + File.separator + "java"
|
||||
+ File.separator + packageName
|
||||
+ File.separator + moduleName
|
||||
+ File.separator + subPackageName
|
||||
);
|
||||
}
|
||||
|
||||
// subPackageName = model.entity => model/entity
|
||||
path = path.replace(".", File.separator);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染模板,生成代码内容。
|
||||
*
|
||||
* @param templateConfig 模板配置
|
||||
* @param genTable 表生成配置
|
||||
* @param fieldConfigs 字段配置
|
||||
* @param pageType 前端页面类型
|
||||
* @return 渲染后的代码内容
|
||||
*/
|
||||
private String getCodeContent(String templatePath,
|
||||
String subpackageName,
|
||||
GenTable genTable,
|
||||
List<GenTableColumn> fieldConfigs,
|
||||
String pageType) {
|
||||
|
||||
Map<String, Object> bindMap = new HashMap<>();
|
||||
|
||||
String entityName = genTable.getEntityName();
|
||||
|
||||
bindMap.put("packageName", genTable.getPackageName());
|
||||
bindMap.put("moduleName", genTable.getModuleName());
|
||||
bindMap.put("subpackageName", subpackageName);
|
||||
bindMap.put("date", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm"));
|
||||
bindMap.put("entityName", entityName);
|
||||
bindMap.put("tableName", genTable.getTableName());
|
||||
bindMap.put("author", genTable.getAuthor());
|
||||
String entityLowerCamel = StrUtil.lowerFirst(entityName);
|
||||
String entityKebab = StrUtil.toSymbolCase(entityName, '-');
|
||||
String entityUpperSnake = StrUtil.toSymbolCase(entityName, '_').toUpperCase();
|
||||
bindMap.put("entityLowerCamel", entityLowerCamel);
|
||||
bindMap.put("entityKebab", entityKebab);
|
||||
bindMap.put("entityUpperSnake", entityUpperSnake);
|
||||
bindMap.put("businessName", genTable.getBusinessName());
|
||||
bindMap.put("entityComment", genTable.getBusinessName());
|
||||
bindMap.put("fieldConfigs", fieldConfigs);
|
||||
|
||||
boolean hasLocalDateTime = false;
|
||||
boolean hasBigDecimal = false;
|
||||
boolean hasRequiredField = false;
|
||||
|
||||
for (GenTableColumn fieldConfig : fieldConfigs) {
|
||||
|
||||
if (StrUtil.isBlank(fieldConfig.getFieldType())) {
|
||||
fieldConfig.setFieldType(JavaTypeEnum.getJavaTypeByColumnType(fieldConfig.getColumnType()));
|
||||
}
|
||||
|
||||
if ("LocalDateTime".equals(fieldConfig.getFieldType()) || "LocalDate".equals(fieldConfig.getFieldType())) {
|
||||
hasLocalDateTime = true;
|
||||
}
|
||||
if ("BigDecimal".equals(fieldConfig.getFieldType())) {
|
||||
hasBigDecimal = true;
|
||||
}
|
||||
if (ObjectUtil.equals(fieldConfig.getIsRequired(), 1)) {
|
||||
hasRequiredField = true;
|
||||
}
|
||||
fieldConfig.setTsType(JavaTypeEnum.getTsTypeByJavaType(fieldConfig.getFieldType()));
|
||||
}
|
||||
|
||||
bindMap.put("hasLocalDateTime", hasLocalDateTime);
|
||||
bindMap.put("hasBigDecimal", hasBigDecimal);
|
||||
bindMap.put("hasRequiredField", hasRequiredField);
|
||||
|
||||
TemplateEngine templateEngine = TemplateUtil.createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH));
|
||||
// 根据 ui 选择不同的前端页面模板:默认 index.vue.vm;封装版使用 index.curd.vue.vm
|
||||
String path = templatePath;
|
||||
if ("curd".equalsIgnoreCase(pageType)) {
|
||||
if (path.endsWith("index.js.vue.vm")) {
|
||||
path = path.replace("index.js.vue.vm", "index.curd.js.vue.vm");
|
||||
} else if (path.endsWith("index.vue.vm")) {
|
||||
path = path.replace("index.vue.vm", "index.curd.vue.vm");
|
||||
}
|
||||
}
|
||||
Template template = templateEngine.getTemplate(path);
|
||||
|
||||
return template.render(bindMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载代码。
|
||||
*
|
||||
* @param tableNames 表名数组,支持多张表
|
||||
* @param ui 页面类型
|
||||
* @return zip 压缩文件字节数组
|
||||
*/
|
||||
@Override
|
||||
public byte[] downloadCode(String[] tableNames, String ui, String type) {
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zip = new ZipOutputStream(outputStream)) {
|
||||
|
||||
// 遍历每个表名,生成对应的代码并压缩到 zip 文件中
|
||||
for (String tableName : tableNames) {
|
||||
generateAndZipCode(tableName, zip, ui, type);
|
||||
}
|
||||
// 确保所有压缩数据写入输出流,避免数据残留在内存缓冲区引发的数据不完整
|
||||
zip.finish();
|
||||
return outputStream.toByteArray();
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Error while generating zip for code download", e);
|
||||
throw new RuntimeException("Failed to generate code zip file1", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据表名生成代码并压缩到 zip 文件中。
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param zip 压缩文件输出流
|
||||
* @param ui 页面类型
|
||||
*/
|
||||
private void generateAndZipCode(String tableName, ZipOutputStream zip, String ui, String type) {
|
||||
List<CodegenPreviewVO> codePreviewList = getCodegenPreviewData(tableName, ui, type);
|
||||
|
||||
for (CodegenPreviewVO codePreview : codePreviewList) {
|
||||
String fileName = codePreview.getFileName();
|
||||
String content = codePreview.getContent();
|
||||
String path = codePreview.getPath();
|
||||
|
||||
try {
|
||||
// 创建压缩条目
|
||||
ZipEntry zipEntry = new ZipEntry(path + File.separator + fileName);
|
||||
zip.putNextEntry(zipEntry);
|
||||
|
||||
// 写入文件内容
|
||||
zip.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// 关闭当前压缩条目
|
||||
zip.closeEntry();
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Error while adding file1 {} to zip", fileName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.youlai.boot.codegen.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.boot.codegen.mapper.GenTableColumnMapper;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import com.youlai.boot.codegen.service.GenTableColumnService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 代码生成字段配置服务实现类
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GenTableColumnServiceImpl extends ServiceImpl<GenTableColumnMapper, GenTableColumn> implements GenTableColumnService {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package com.youlai.boot.codegen.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.boot.YouLaiBootApplication;
|
||||
import com.youlai.boot.common.enums.EnvEnum;
|
||||
import com.youlai.boot.codegen.enums.FormTypeEnum;
|
||||
import com.youlai.boot.codegen.enums.JavaTypeEnum;
|
||||
import com.youlai.boot.codegen.enums.QueryTypeEnum;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.codegen.config.CodegenProperties;
|
||||
import com.youlai.boot.codegen.converter.CodegenConverter;
|
||||
import com.youlai.boot.codegen.mapper.DatabaseMapper;
|
||||
import com.youlai.boot.codegen.mapper.GenTableMapper;
|
||||
import com.youlai.boot.codegen.model.vo.ColumnMetaVO;
|
||||
import com.youlai.boot.codegen.model.vo.TableMetaVO;
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import com.youlai.boot.codegen.model.form.GenConfigForm;
|
||||
import com.youlai.boot.codegen.service.GenTableService;
|
||||
import com.youlai.boot.codegen.service.GenTableColumnService;
|
||||
import com.youlai.boot.system.service.MenuService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据库服务实现类
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> implements GenTableService {
|
||||
|
||||
private final DatabaseMapper databaseMapper;
|
||||
private final CodegenProperties codegenProperties;
|
||||
private final GenTableColumnService genTableColumnService;
|
||||
private final CodegenConverter codegenConverter;
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String springProfilesActive;
|
||||
|
||||
private final MenuService menuService;
|
||||
|
||||
/**
|
||||
* 获取代码生成配置
|
||||
*
|
||||
* @param tableName 表名 eg: sys_user
|
||||
* @return 代码生成配置
|
||||
*/
|
||||
@Override
|
||||
public GenConfigForm getGenTableFormData(String tableName) {
|
||||
// 查询表生成配置
|
||||
GenTable genTable = this.getOne(
|
||||
new LambdaQueryWrapper<>(GenTable.class)
|
||||
.eq(GenTable::getTableName, tableName)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
|
||||
// 是否有代码生成配置
|
||||
boolean hasGenTable = genTable != null;
|
||||
|
||||
// 如果没有代码生成配置,则根据表的元数据生成默认配置
|
||||
if (genTable == null) {
|
||||
TableMetaVO tableMetadata = databaseMapper.getTableMetadata(tableName);
|
||||
Assert.isTrue(tableMetadata != null, "未找到表元数据");
|
||||
|
||||
genTable = new GenTable();
|
||||
genTable.setTableName(tableName);
|
||||
|
||||
// 表注释作为业务名称,去掉表字 例如:用户表 -> 用户
|
||||
String tableComment = tableMetadata.getTableComment();
|
||||
if (StrUtil.isNotBlank(tableComment)) {
|
||||
genTable.setBusinessName(tableComment.replace("表", "").trim());
|
||||
}
|
||||
// 根据表名生成实体类名,支持去除前缀 例如:sys_user -> SysUser
|
||||
String removePrefix = genTable.getRemoveTablePrefix();
|
||||
String processedTable = tableName;
|
||||
if (StrUtil.isNotBlank(removePrefix) && StrUtil.startWith(tableName, removePrefix)) {
|
||||
processedTable = StrUtil.removePrefix(tableName, removePrefix);
|
||||
}
|
||||
genTable.setEntityName(StrUtil.toCamelCase(StrUtil.upperFirst(StrUtil.toCamelCase(processedTable))));
|
||||
|
||||
genTable.setPackageName(YouLaiBootApplication.class.getPackageName());
|
||||
genTable.setModuleName(codegenProperties.getDefaultConfig().getModuleName()); // 默认模块名
|
||||
genTable.setAuthor(codegenProperties.getDefaultConfig().getAuthor());
|
||||
}
|
||||
|
||||
// 根据表的列 + 已经存在的字段生成配置 得到 组合后的字段生成配置
|
||||
List<GenTableColumn> genTableColumns = new ArrayList<>();
|
||||
|
||||
// 获取表的列
|
||||
List<ColumnMetaVO> tableColumns = databaseMapper.getTableColumns(tableName);
|
||||
if (CollectionUtil.isNotEmpty(tableColumns)) {
|
||||
// 查询字段生成配置
|
||||
List<GenTableColumn> fieldConfigList = genTableColumnService.list(
|
||||
new LambdaQueryWrapper<GenTableColumn>()
|
||||
.eq(GenTableColumn::getTableId, genTable.getId())
|
||||
.orderByAsc(GenTableColumn::getFieldSort)
|
||||
);
|
||||
Integer maxSort = fieldConfigList.stream()
|
||||
.map(GenTableColumn::getFieldSort)
|
||||
.filter(Objects::nonNull) // 过滤掉空值
|
||||
.max(Integer::compareTo)
|
||||
.orElse(0);
|
||||
for (ColumnMetaVO tableColumn : tableColumns) {
|
||||
// 根据列名获取字段生成配置
|
||||
String columnName = tableColumn.getColumnName();
|
||||
GenTableColumn fieldConfig = fieldConfigList.stream()
|
||||
.filter(item -> StrUtil.equals(item.getColumnName(), columnName))
|
||||
.findFirst()
|
||||
.orElseGet(() -> createDefaultFieldConfig(tableColumn));
|
||||
if (fieldConfig.getFieldSort() == null) {
|
||||
fieldConfig.setFieldSort(++maxSort);
|
||||
}
|
||||
// 根据列类型设置字段类型
|
||||
String fieldType = fieldConfig.getFieldType();
|
||||
if (StrUtil.isBlank(fieldType)) {
|
||||
String javaType = JavaTypeEnum.getJavaTypeByColumnType(fieldConfig.getColumnType());
|
||||
fieldConfig.setFieldType(javaType);
|
||||
}
|
||||
// 如果没有代码生成配置,则默认展示在列表和表单
|
||||
if (!hasGenTable) {
|
||||
fieldConfig.setIsShowInList(1);
|
||||
fieldConfig.setIsShowInForm(1);
|
||||
}
|
||||
genTableColumns.add(fieldConfig);
|
||||
}
|
||||
}
|
||||
// 对 genTableColumns 按照 fieldSort 排序
|
||||
genTableColumns = genTableColumns.stream().sorted(Comparator.comparing(GenTableColumn::getFieldSort)).toList();
|
||||
GenConfigForm genConfigForm = codegenConverter.toGenConfigForm(genTable, genTableColumns);
|
||||
|
||||
genConfigForm.setFrontendAppName(codegenProperties.getFrontendAppName());
|
||||
genConfigForm.setBackendAppName(codegenProperties.getBackendAppName());
|
||||
return genConfigForm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建默认字段配置
|
||||
*
|
||||
* @param columnMetaVO 表字段元数据
|
||||
* @return
|
||||
*/
|
||||
private GenTableColumn createDefaultFieldConfig(ColumnMetaVO columnMetaVO) {
|
||||
GenTableColumn fieldConfig = new GenTableColumn();
|
||||
fieldConfig.setColumnName(columnMetaVO.getColumnName());
|
||||
fieldConfig.setColumnType(columnMetaVO.getDataType());
|
||||
fieldConfig.setFieldComment(columnMetaVO.getColumnComment());
|
||||
fieldConfig.setFieldName(StrUtil.toCamelCase(columnMetaVO.getColumnName()));
|
||||
fieldConfig.setIsRequired("YES".equals(columnMetaVO.getIsNullable()) ? 0 : 1);
|
||||
|
||||
String columnType = StrUtil.blankToDefault(fieldConfig.getColumnType(), "").toLowerCase();
|
||||
if ("date".equals(columnType)) {
|
||||
fieldConfig.setFormType(FormTypeEnum.DATE);
|
||||
} else if ("datetime".equals(columnType) || "timestamp".equals(columnType)) {
|
||||
fieldConfig.setFormType(FormTypeEnum.DATE_TIME);
|
||||
} else {
|
||||
fieldConfig.setFormType(FormTypeEnum.INPUT);
|
||||
}
|
||||
|
||||
fieldConfig.setQueryType(QueryTypeEnum.EQ);
|
||||
fieldConfig.setMaxLength(columnMetaVO.getCharacterMaximumLength());
|
||||
return fieldConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存代码生成配置
|
||||
*
|
||||
* @param formData 代码生成配置表单
|
||||
*/
|
||||
@Override
|
||||
public void saveGenConfig(GenConfigForm formData) {
|
||||
GenTable genTable = codegenConverter.toGenTable(formData);
|
||||
this.saveOrUpdate(genTable);
|
||||
|
||||
// 如果选择上级菜单且当前环境不是生产环境,则保存菜单
|
||||
Long parentMenuId = formData.getParentMenuId();
|
||||
if (parentMenuId != null && !EnvEnum.PROD.getValue().equals(springProfilesActive)) {
|
||||
menuService.addMenuForCodegen(parentMenuId, genTable);
|
||||
}
|
||||
|
||||
List<GenTableColumn> genTableColumns = codegenConverter.toGenTableColumn(formData.getFieldConfigs());
|
||||
|
||||
if (CollectionUtil.isEmpty(genTableColumns)) {
|
||||
throw new BusinessException("字段配置不能为空");
|
||||
}
|
||||
genTableColumns.forEach(genTableColumn -> {
|
||||
genTableColumn.setTableId(genTable.getId());
|
||||
});
|
||||
genTableColumnService.saveOrUpdateBatch(genTableColumns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除代码生成配置
|
||||
*
|
||||
* @param tableName 表名
|
||||
*/
|
||||
@Override
|
||||
public void deleteGenConfig(String tableName) {
|
||||
GenTable genTable = this.getOne(new LambdaQueryWrapper<GenTable>()
|
||||
.eq(GenTable::getTableName, tableName));
|
||||
|
||||
boolean result = this.remove(new LambdaQueryWrapper<GenTable>()
|
||||
.eq(GenTable::getTableName, tableName)
|
||||
);
|
||||
if (result) {
|
||||
genTableColumnService.remove(new LambdaQueryWrapper<GenTableColumn>()
|
||||
.eq(GenTableColumn::getTableId, genTable.getId())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.youlai.boot.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 数据权限注解
|
||||
*
|
||||
* @author zc
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
public @interface DataPermission {
|
||||
|
||||
/**
|
||||
* 数据权限 {@link com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor}
|
||||
*/
|
||||
String deptAlias() default "";
|
||||
|
||||
String deptIdColumnName() default "dept_id";
|
||||
|
||||
String userAlias() default "";
|
||||
|
||||
String userIdColumnName() default "create_by";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.youlai.boot.common.annotation;
|
||||
|
||||
import com.youlai.boot.common.enums.ActionTypeEnum;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 日志注解
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2024/6/25
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface Log {
|
||||
|
||||
/**
|
||||
* 模块
|
||||
*
|
||||
* @return 模块
|
||||
*/
|
||||
LogModuleEnum module();
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
*
|
||||
* @return 操作类型
|
||||
*/
|
||||
ActionTypeEnum value();
|
||||
|
||||
/**
|
||||
* 操作标题(可选,默认使用枚举描述)
|
||||
*
|
||||
* @return 标题
|
||||
*/
|
||||
String title() default "";
|
||||
|
||||
/**
|
||||
* 自定义日志内容(可选,用于记录操作细节)
|
||||
*
|
||||
* @return 日志内容
|
||||
*/
|
||||
String content() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.youlai.boot.common.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 防止重复提交注解
|
||||
* <p>
|
||||
* 该注解用于方法上,防止在指定时间内的重复提交。 默认时间为5秒。
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface RepeatSubmit {
|
||||
|
||||
/**
|
||||
* 锁过期时间(秒)
|
||||
* <p>
|
||||
* 默认5秒内不允许重复提交
|
||||
*/
|
||||
int expire() default 5;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.youlai.boot.common.annotation;
|
||||
|
||||
import com.youlai.boot.common.validator.FieldValidator;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 用于验证字段值是否合法的注解
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.18.0
|
||||
*/
|
||||
@Documented
|
||||
@Constraint(validatedBy = FieldValidator.class)
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ValidField {
|
||||
|
||||
/**
|
||||
* 验证失败时的错误信息。
|
||||
*/
|
||||
String message() default "非法字段";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
/**
|
||||
* 允许的合法值列表。
|
||||
*/
|
||||
String[] allowedValues();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.youlai.boot.common.aspect;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import com.youlai.boot.common.annotation.Log;
|
||||
import com.youlai.boot.common.enums.ActionTypeEnum;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
import com.youlai.boot.common.util.IPUtils;
|
||||
import com.youlai.boot.framework.security.util.SecurityUtils;
|
||||
import com.youlai.boot.system.model.entity.SysLog;
|
||||
import com.youlai.boot.system.service.LogService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 日志切面
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class LogAspect {
|
||||
|
||||
private final LogService logService;
|
||||
|
||||
/**
|
||||
* 日志注解切点
|
||||
*/
|
||||
@Pointcut("@annotation(logAnnotation)")
|
||||
public void logPointCut(Log logAnnotation) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕通知:记录操作日志
|
||||
*/
|
||||
@Around(value = "logPointCut(logAnnotation)", argNames = "pjp,logAnnotation")
|
||||
public Object around(ProceedingJoinPoint pjp, Log logAnnotation) throws Throwable {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 在方法执行前获取用户信息,避免 logout 等操作清除 SecurityContext 后无法获取
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
String username = SecurityUtils.getUsername();
|
||||
Object result = null;
|
||||
Exception exception = null;
|
||||
|
||||
try {
|
||||
result = pjp.proceed();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
throw e;
|
||||
} finally {
|
||||
long executionTime = System.currentTimeMillis() - startTime;
|
||||
// fallback:登录等场景在 proceed() 前未认证,需在 proceed() 后获取
|
||||
if (userId == null) {
|
||||
userId = SecurityUtils.getUserId();
|
||||
username = SecurityUtils.getUsername();
|
||||
}
|
||||
try {
|
||||
saveLogAsync(logAnnotation, executionTime, exception, userId, username);
|
||||
} catch (Exception ex) {
|
||||
log.error("保存操作日志失败", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步保存日志
|
||||
*/
|
||||
@Async
|
||||
public void saveLogAsync(Log logAnnotation, long executionTime, Exception exception, Long userId, String username) {
|
||||
try {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
|
||||
// 解析 User-Agent
|
||||
String userAgentStr = request.getHeader("User-Agent");
|
||||
UserAgent userAgent = UserAgentUtil.parse(userAgentStr);
|
||||
|
||||
// 解析 IP 地区
|
||||
String ip = IPUtils.getIpAddr(request);
|
||||
String region = IPUtils.getRegion(ip);
|
||||
String province = null;
|
||||
String city = null;
|
||||
if (StrUtil.isNotBlank(region)) {
|
||||
String[] parts = region.split("\\|");
|
||||
if (parts.length >= 3) {
|
||||
province = StrUtil.blankToDefault(parts[2], null);
|
||||
city = StrUtil.blankToDefault(parts[3], null);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建日志实体
|
||||
LogModuleEnum module = logAnnotation.module();
|
||||
ActionTypeEnum actionType = logAnnotation.value();
|
||||
String title = StrUtil.blankToDefault(logAnnotation.title(),
|
||||
module.getLabel() + "-" + actionType.getLabel());
|
||||
String content = logAnnotation.content();
|
||||
|
||||
SysLog logEntity = new SysLog();
|
||||
logEntity.setModule(module);
|
||||
logEntity.setActionType(actionType);
|
||||
logEntity.setTitle(title);
|
||||
logEntity.setContent(content);
|
||||
logEntity.setOperatorId(userId);
|
||||
logEntity.setOperatorName(username);
|
||||
logEntity.setRequestUri(request.getRequestURI());
|
||||
logEntity.setRequestMethod(request.getMethod());
|
||||
logEntity.setIp(ip);
|
||||
logEntity.setProvince(province);
|
||||
logEntity.setCity(city);
|
||||
logEntity.setDevice(userAgent.getOs().getName());
|
||||
logEntity.setOs(userAgent.getOs().getName());
|
||||
logEntity.setBrowser(userAgent.getBrowser().getName());
|
||||
logEntity.setStatus(exception == null ? 1 : 0);
|
||||
logEntity.setErrorMsg(exception != null ? exception.getMessage() : null);
|
||||
logEntity.setExecutionTime((int) executionTime);
|
||||
logEntity.setCreateTime(LocalDateTime.now());
|
||||
|
||||
logService.save(logEntity);
|
||||
} catch (Exception e) {
|
||||
log.error("保存操作日志异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.youlai.boot.common.aspect;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.common.constant.SecurityConstants;
|
||||
import com.youlai.boot.common.result.ResultCode;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.common.annotation.RepeatSubmit;
|
||||
import com.youlai.boot.common.util.IPUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 防重复提交切面
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class RepeatSubmitAspect {
|
||||
|
||||
private final RedissonClient redissonClient;
|
||||
|
||||
/**
|
||||
* 防重复提交切点
|
||||
*/
|
||||
@Pointcut("@annotation(repeatSubmit)")
|
||||
public void repeatSubmitPointCut(RepeatSubmit repeatSubmit) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕通知:处理防重复提交逻辑
|
||||
*/
|
||||
@Around(value = "repeatSubmitPointCut(repeatSubmit)", argNames = "pjp,repeatSubmit")
|
||||
public Object handleRepeatSubmit(ProceedingJoinPoint pjp, RepeatSubmit repeatSubmit) throws Throwable {
|
||||
String lockKey = buildLockKey();
|
||||
|
||||
int expire = repeatSubmit.expire();
|
||||
RLock lock = redissonClient.getLock(lockKey);
|
||||
|
||||
boolean locked = lock.tryLock(0, expire, TimeUnit.SECONDS);
|
||||
if (!locked) {
|
||||
throw new BusinessException(ResultCode.DUPLICATE_SUBMISSION);
|
||||
}
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成防重复提交锁的 key
|
||||
* @return 锁的 key
|
||||
*/
|
||||
private String buildLockKey() {
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||
// 用户唯一标识
|
||||
String userIdentifier = getUserIdentifier(request);
|
||||
// 请求唯一标识 = 请求方法 + 请求路径 + 请求参数(严谨的做法)
|
||||
String requestIdentifier = StrUtil.join(":", request.getMethod(), request.getRequestURI());
|
||||
return StrUtil.format(RedisConstants.Lock.RESUBMIT, userIdentifier, requestIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户唯一标识
|
||||
* 1. 从请求头中获取 Token,使用 SHA-256 加密 Token 作为用户唯一标识
|
||||
* 2. 如果 Token 为空,使用 IP 作为用户唯一标识
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 用户唯一标识
|
||||
*/
|
||||
private String getUserIdentifier(HttpServletRequest request) {
|
||||
// 用户身份唯一标识
|
||||
String userIdentifier;
|
||||
// 从请求头中获取 Token
|
||||
String tokenHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
if (StrUtil.isNotBlank(tokenHeader) && tokenHeader.startsWith(SecurityConstants.BEARER_TOKEN_PREFIX)) {
|
||||
String rawToken = tokenHeader.substring(SecurityConstants.BEARER_TOKEN_PREFIX.length()); // 去掉 Bearer 后的 Token
|
||||
userIdentifier = DigestUtil.sha256Hex(rawToken); // 使用 SHA-256 加密 Token 作为用户唯一标识
|
||||
} else {
|
||||
userIdentifier = IPUtils.getIpAddr(request); // 使用 IP 作为用户唯一标识
|
||||
}
|
||||
return userIdentifier;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.youlai.boot.common.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 基础实体类
|
||||
*
|
||||
* <p>实体类的基类,包含了实体类的公共属性,如创建时间、更新时间、逻辑删除标识等</p>
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2024/6/23
|
||||
*/
|
||||
@Data
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@JsonInclude(value = JsonInclude.Include.NON_NULL)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@JsonInclude(value = JsonInclude.Include.NON_NULL)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.youlai.boot.common.base;
|
||||
|
||||
import com.youlai.boot.common.annotation.ValidField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@Schema
|
||||
public class BaseQuery implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "页码", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
|
||||
private Integer pageNum = 1;
|
||||
|
||||
@Schema(description = "每页记录数", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "10")
|
||||
private Integer pageSize = 10;
|
||||
|
||||
@Schema(description = "排序字段", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
@ValidField(allowedValues = {"create_time", "update_time"})
|
||||
private String sortBy;
|
||||
|
||||
@Schema(description = "排序方式(正序:ASC;反序:DESC)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String order;
|
||||
|
||||
public boolean isPaged() {
|
||||
return pageNum != null && pageSize != null && pageSize > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.youlai.boot.common.base;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 枚举通用接口
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/3/27 12:06
|
||||
*/
|
||||
public interface IBaseEnum<T> {
|
||||
|
||||
T getValue();
|
||||
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* 根据值获取枚举
|
||||
*
|
||||
* @param value
|
||||
* @param clazz
|
||||
* @param <E> 枚举
|
||||
* @return
|
||||
*/
|
||||
static <E extends Enum<E> & IBaseEnum> E getEnumByValue(Object value, Class<E> clazz) {
|
||||
Objects.requireNonNull(value);
|
||||
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
|
||||
E matchEnum = allEnums.stream()
|
||||
.filter(e -> ObjectUtil.equal(e.getValue(), value))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
return matchEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文本标签获取值
|
||||
*
|
||||
* @param value
|
||||
* @param clazz
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
static <E extends Enum<E> & IBaseEnum> String getLabelByValue(Object value, Class<E> clazz) {
|
||||
Objects.requireNonNull(value);
|
||||
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
|
||||
E matchEnum = allEnums.stream()
|
||||
.filter(e -> ObjectUtil.equal(e.getValue(), value))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
String label = null;
|
||||
if (matchEnum != null) {
|
||||
label = matchEnum.getLabel();
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据文本标签获取值
|
||||
*
|
||||
* @param label
|
||||
* @param clazz
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
static <E extends Enum<E> & IBaseEnum> Object getValueByLabel(String label, Class<E> clazz) {
|
||||
Objects.requireNonNull(label);
|
||||
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
|
||||
String finalLabel = label;
|
||||
E matchEnum = allEnums.stream()
|
||||
.filter(e -> ObjectUtil.equal(e.getLabel(), finalLabel))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
Object value = null;
|
||||
if (matchEnum != null) {
|
||||
value = matchEnum.getValue();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.youlai.boot.common.constant;
|
||||
|
||||
/**
|
||||
* JWT Claims声明常量
|
||||
* <p>
|
||||
* JWT Claims 属于 Payload 的一部分,包含了一些实体(通常指的用户)的状态和额外的元数据。
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2023/11/24
|
||||
*/
|
||||
public interface JwtClaimConstants {
|
||||
|
||||
/**
|
||||
* 令牌类型
|
||||
*/
|
||||
String TOKEN_TYPE = "tokenType";
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
String USER_ID = "userId";
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
String DEPT_ID = "deptId";
|
||||
|
||||
/**
|
||||
* 数据权限列表
|
||||
* <p>
|
||||
* 存储用户所有角色的数据权限范围,用于实现多角色权限合并(并集策略)
|
||||
*/
|
||||
String DATA_SCOPES = "dataScopes";
|
||||
|
||||
/**
|
||||
* 权限(角色Code)集合
|
||||
*/
|
||||
String AUTHORITIES = "authorities";
|
||||
|
||||
/**
|
||||
* Token 版本号
|
||||
* <p>
|
||||
* 用于用户级会话失效,当用户修改密码、被禁用、强制下线时递增版本号,
|
||||
* 使该用户之前签发的所有 Token 失效。
|
||||
*/
|
||||
String TOKEN_VERSION = "tokenVersion";
|
||||
|
||||
/**
|
||||
* 认证主体类型
|
||||
*/
|
||||
String SUBJECT_TYPE = "subjectType";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.youlai.boot.common.constant;
|
||||
|
||||
/**
|
||||
* Redis 常量
|
||||
*
|
||||
* @author Theo
|
||||
* @since 2024-7-29 11:46:08
|
||||
*/
|
||||
public interface RedisConstants {
|
||||
|
||||
/**
|
||||
* 限流相关键
|
||||
*/
|
||||
interface RateLimiter {
|
||||
String IP = "rate_limiter:ip:{}"; // IP限流(示例:rate_limiter:ip:192.168.1.1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 分布式锁相关键
|
||||
*/
|
||||
interface Lock {
|
||||
String RESUBMIT = "lock:resubmit:{}:{}"; // 防重复提交(示例:lock:resubmit:userIdentifier:requestIdentifier)
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证模块
|
||||
*/
|
||||
interface Auth {
|
||||
// 存储访问令牌对应的用户会话信息(accessToken -> UserSession)
|
||||
String ACCESS_TOKEN_USER = "auth:token:access:{}";
|
||||
// 存储刷新令牌对应的用户会话信息(refreshToken -> UserSession)
|
||||
String REFRESH_TOKEN_USER = "auth:token:refresh:{}";
|
||||
// 用户与访问令牌的映射(userId -> accessToken)
|
||||
String USER_ACCESS_TOKEN = "auth:user:access:{}";
|
||||
// 用户与刷新令牌的映射(userId -> refreshToken
|
||||
String USER_REFRESH_TOKEN = "auth:user:refresh:{}";
|
||||
// 已撤销 Token 的 JTI(单端退出/会话注销):如果 jti 在撤销列表中,则 Token 立即无效
|
||||
String BLACKLIST_TOKEN = "auth:token:blacklist:{}";
|
||||
String REVOKED_JTI = BLACKLIST_TOKEN;
|
||||
// 用户 Token 版本号(用于按用户失效历史 JWT):token.tokenVersion != redis.tokenVersion => token 无效
|
||||
String USER_TOKEN_VERSION = "auth:user:token_version:{}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码模块
|
||||
*/
|
||||
interface Captcha {
|
||||
String IMAGE_CODE = "captcha:image:{}"; // 图形验证码
|
||||
String SMS_LOGIN_CODE = "captcha:sms_login:{}"; // 登录短信验证码
|
||||
String SMS_REGISTER_CODE = "captcha:sms_register:{}";// 注册短信验证码
|
||||
String EMAIL_LOGIN_CODE = "captcha:email_login:{}"; // 登录邮箱验证码
|
||||
String EMAIL_REGISTER_CODE = "captcha:email_register:{}"; // 注册邮箱验证码
|
||||
String MOBILE_CODE = "captcha:mobile:{}"; // 绑定、更换手机验证码
|
||||
String EMAIL_CODE = "captcha:email:{}"; // 邮箱验证码
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统模块
|
||||
*/
|
||||
interface System {
|
||||
String CONFIG = "system:config"; // 系统配置
|
||||
String ROLE_PERMS = "system:role:perms"; // 系统角色和权限映射
|
||||
}
|
||||
|
||||
/**
|
||||
* 开放接口模块
|
||||
*/
|
||||
interface OpenApi {
|
||||
String ACCESS_TOKEN = "openapi:token:access:{}";
|
||||
String IDEMPOTENCY_LOCK = "openapi:idempotency:lock:{}:{}";
|
||||
String IDEMPOTENCY_RESULT = "openapi:idempotency:result:{}:{}";
|
||||
String RATE_LIMIT_AUTH_IP = "openapi:rate-limit:auth-ip:{}";
|
||||
String RATE_LIMIT_APP = "openapi:rate-limit:app:{}";
|
||||
String SANDBOX_ACCOUNT = "openapi:sandbox:account:{}";
|
||||
String SANDBOX_ACCOUNT_FLOWS = "openapi:sandbox:account:flows:{}";
|
||||
String SANDBOX_ORDER_DETAIL = "openapi:sandbox:order:detail:{}:{}";
|
||||
String SANDBOX_ORDER_LIST = "openapi:sandbox:orders:{}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.youlai.boot.common.constant;
|
||||
|
||||
/**
|
||||
* 安全模块常量
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2023/11/24
|
||||
*/
|
||||
public interface SecurityConstants {
|
||||
|
||||
/**
|
||||
* 登录路径
|
||||
*/
|
||||
String LOGIN_PATH = "/api/v1/auth/login";
|
||||
|
||||
/**
|
||||
* JWT Token 前缀
|
||||
*/
|
||||
String BEARER_TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
/**
|
||||
* 角色前缀,用于区分 authorities 角色和权限, ROLE_* 角色 、没有前缀的是权限
|
||||
*/
|
||||
String ROLE_PREFIX = "ROLE_";
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user