commit 0d94fd5a3745512e9c42ad3c58404a73ed564f92 Author: chong-de.fang Date: Thu Mar 2 11:52:48 2023 +0800 init diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..840ea4d --- /dev/null +++ b/pom.xml @@ -0,0 +1,362 @@ + + + 4.0.0 + + com.rzdata + rzdata-archetype + 4.5.0 + + Rzdata-Archetype + https://gitee.com/JavaLionLi/RuoYi-Vue-Plus + 后端服务脚手架 + + + 4.5.0 + 2.7.7 + UTF-8 + UTF-8 + 1.8 + 3.2.2 + 2.2.2 + 1.6.14 + 5.2.3 + 3.1.5 + 2.3 + 1.34.0 + 3.5.3.1 + 3.9.1 + 5.8.11 + 4.10.0 + 2.7.10 + 3.19.1 + 2.2.3 + 3.5.2 + 2.14.2 + 2.3.1 + 1.18.24 + + + 1.33 + + + 1.12.373 + + 2.0.23 + 3.1.660 + + + + + local + + + local + debug + + + + dev + + + dev + debug + + + + true + + + + prod + + prod + warn + + + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + cn.hutool + hutool-bom + ${hutool.version} + pom + import + + + + org.springdoc + springdoc-openapi-webmvc-core + ${springdoc.version} + + + + org.springdoc + springdoc-openapi-javadoc + ${springdoc.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + org.apache.poi + poi + ${poi.version} + + + org.apache.poi + poi-ooxml + ${poi.version} + + + com.alibaba + easyexcel + ${easyexcel.version} + + + org.apache.poi + poi-ooxml-schemas + + + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + cn.dev33 + sa-token-spring-boot-starter + ${satoken.version} + + + + cn.dev33 + sa-token-jwt + ${satoken.version} + + + cn.hutool + hutool-all + + + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + ${dynamic-ds.version} + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + p6spy + p6spy + ${p6spy.version} + + + + com.squareup.okhttp3 + okhttp + ${okhttp.version} + + + + com.amazonaws + aws-java-sdk-s3 + ${aws-java-sdk-s3.version} + + + + com.aliyun + dysmsapi20170525 + ${aliyun.sms.version} + + + + com.tencentcloudapi + tencentcloud-sdk-java-sms + ${tencent.sms.version} + + + + de.codecentric + spring-boot-admin-starter-server + ${spring-boot-admin.version} + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + org.redisson + redisson-spring-boot-starter + ${redisson.version} + + + org.redisson + redisson-spring-data-30 + + + + + org.redisson + redisson-spring-data-27 + ${redisson.version} + + + com.baomidou + lock4j-redisson-spring-boot-starter + ${lock4j.version} + + + + + com.xuxueli + xxl-job-core + ${xxl-job.version} + + + + com.alibaba + transmittable-thread-local + ${alibaba-ttl.version} + + + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + + + + + service + + pom + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.9.0 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + com.github.therapi + therapi-runtime-javadoc-scribe + 0.15.0 + + + org.projectlombok + lombok + ${lombok.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + -Dfile.encoding=UTF-8 + + ${profiles.active} + + exclude + + + + + + src/main/resources + + false + + + src/main/resources + + + application* + bootstrap* + banner* + + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public/ + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public/ + + true + + + false + + + + + diff --git a/script/bin/ry.bat b/script/bin/ry.bat new file mode 100644 index 0000000..94434a9 --- /dev/null +++ b/script/bin/ry.bat @@ -0,0 +1,68 @@ +rem 使用者应根据自身平台编码自行转换 防止乱码 例如 win使用gbk编码 +@echo off + +rem jar平级目录 +set AppName=ruoyi-admin.jar + +rem JVM参数 +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" + + +ECHO. + ECHO. [1] 启动%AppName% + ECHO. [2] 关闭%AppName% + ECHO. [3] 重启%AppName% + ECHO. [4] 启动状态 %AppName% + ECHO. [5] 退 出 +ECHO. + +ECHO.请输入选择项目的序号: +set /p ID= + IF "%id%"=="1" GOTO start + IF "%id%"=="2" GOTO stop + IF "%id%"=="3" GOTO restart + IF "%id%"=="4" GOTO status + IF "%id%"=="5" EXIT +PAUSE +:start + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if defined pid ( + echo %%is running + PAUSE + ) + +start javaw %JVM_OPTS% -jar %AppName% + +echo starting…… +echo Start %AppName% success... +goto:eof + +rem 函数stop通过jps命令查找pid并结束进程 +:stop + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% does not exists) else ( + echo prepare to kill %image_name% + echo start kill %pid% ... + rem 根据进程ID,kill进程 + taskkill /f /pid %pid% + ) +goto:eof +:restart + call :stop + call :start +goto:eof +:status + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% is dead ) else ( + echo %image_name% is running + ) +goto:eof diff --git a/script/bin/ry.sh b/script/bin/ry.sh new file mode 100644 index 0000000..0cf5ce9 --- /dev/null +++ b/script/bin/ry.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# ./ry.sh start 启动 stop 停止 restart 重启 status 状态 +AppName=ruoyi-admin.jar + +# JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/script/docker/database.yml b/script/docker/database.yml new file mode 100644 index 0000000..0368fd2 --- /dev/null +++ b/script/docker/database.yml @@ -0,0 +1,61 @@ +version: '3' + +services: + # 此镜像仅用于测试 正式环境需自行安装数据库 + # SID: XE user: system password: oracle + oracle: + image: tekintian/oracle12c:latest + container_name: oracle + environment: + # 时区上海 + TZ: Asia/Shanghai + DBCA_TOTAL_MEMORY: 16192 + ports: + - "18080:8080" + - "1521:1521" + volumes: + # 数据挂载 + - "/docker/oracle/data:/u01/app/oracle" + network_mode: "host" + + # 此镜像仅用于测试 正式环境需自行安装数据库 + sqlserver: + image: mcr.microsoft.com/mssql/server:2017-latest + container_name: sqlserver + environment: + # 时区上海 + TZ: Asia/Shanghai + ACCEPT_EULA: "Y" + SA_PASSWORD: "Ruoyi@123" + ports: + - "1433:1433" + volumes: + # 数据挂载 + - "/docker/sqlserver/data:/var/opt/mssql" + network_mode: "host" + + postgres: + image: postgres:14.2 + container_name: postgres + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: root + POSTGRES_DB: postgres + ports: + - "5432:5432" + volumes: + - /docker/postgres/data:/var/lib/postgresql/data + network_mode: "host" + + postgres13: + image: postgres:13.6 + container_name: postgres13 + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: root + POSTGRES_DB: postgres + ports: + - "5433:5432" + volumes: + - /docker/postgres13/data:/var/lib/postgresql/data + network_mode: "host" diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml new file mode 100644 index 0000000..ea8e529 --- /dev/null +++ b/script/docker/docker-compose.yml @@ -0,0 +1,154 @@ +version: '3' + +services: + mysql: + image: mysql:8.0.31 + container_name: mysql + environment: + # 时区上海 + TZ: Asia/Shanghai + # root 密码 + MYSQL_ROOT_PASSWORD: root + # 初始化数据库(后续的初始化sql会在这个库执行) + MYSQL_DATABASE: ry-vue + ports: + - "3306:3306" + volumes: + # 数据挂载 + - /docker/mysql/data/:/var/lib/mysql/ + # 配置挂载 + - /docker/mysql/conf/:/etc/mysql/conf.d/ + command: + # 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配) + --default-authentication-plugin=mysql_native_password + --character-set-server=utf8mb4 + --collation-server=utf8mb4_general_ci + --explicit_defaults_for_timestamp=true + --lower_case_table_names=1 + privileged: true + network_mode: "host" + + nginx-web: + image: nginx:1.22.1 + container_name: nginx-web + environment: + # 时区上海 + TZ: Asia/Shanghai + ports: + - "80:80" + - "443:443" + volumes: + # 证书映射 + - /docker/nginx/cert:/etc/nginx/cert + # 配置文件映射 + - /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf + # 页面目录 + - /docker/nginx/html:/usr/share/nginx/html + # 日志目录 + - /docker/nginx/log:/var/log/nginx + privileged: true + network_mode: "host" + + redis: + image: redis:6.2.7 + container_name: redis + ports: + - "6379:6379" + environment: + # 时区上海 + TZ: Asia/Shanghai + volumes: + # 配置文件 + - /docker/redis/conf:/redis/config:rw + # 数据文件 + - /docker/redis/data/:/redis/data/:rw + command: "redis-server /redis/config/redis.conf" + privileged: true + network_mode: "host" + + minio: + image: minio/minio:RELEASE.2022-05-26T05-48-41Z + container_name: minio + ports: + # api 端口 + - "9000:9000" + # 控制台端口 + - "9001:9001" + environment: + # 时区上海 + TZ: Asia/Shanghai + # 管理后台用户名 + MINIO_ROOT_USER: rzdata + # 管理后台密码,最小8个字符 + MINIO_ROOT_PASSWORD: ruoyi123 + # https需要指定域名 + #MINIO_SERVER_URL: "https://xxx.com:9000" + #MINIO_BROWSER_REDIRECT_URL: "https://xxx.com:9001" + # 开启压缩 on 开启 off 关闭 + MINIO_COMPRESS: "off" + # 扩展名 .pdf,.doc 为空 所有类型均压缩 + MINIO_COMPRESS_EXTENSIONS: "" + # mime 类型 application/pdf 为空 所有类型均压缩 + MINIO_COMPRESS_MIME_TYPES: "" + volumes: + # 映射当前目录下的data目录至容器内/data目录 + - /docker/minio/data:/data + # 映射配置目录 + - /docker/minio/config:/root/.minio/ + command: server --address ':9000' --console-address ':9001' /data # 指定容器中的目录 /data + privileged: true + network_mode: "host" + + ruoyi-server1: + image: ruoyi/ruoyi-server:4.5.0 + container_name: rzdata-server1 + environment: + # 时区上海 + TZ: Asia/Shanghai + SERVER_PORT: 8080 + volumes: + # 配置文件 + - /docker/server1/logs/:/rzdata/server/logs/ + # skywalking 探针 +# - /docker/skywalking/agent/:/rzdata/skywalking/agent + privileged: true + network_mode: "host" + + ruoyi-server2: + image: "ruoyi/ruoyi-server:4.5.0" + container_name: rzdata-server2 + environment: + # 时区上海 + TZ: Asia/Shanghai + SERVER_PORT: 8081 + volumes: + # 配置文件 + - /docker/server2/logs/:/rzdata/server/logs/ + # skywalking 探针 +# - /docker/skywalking/agent/:/rzdata/skywalking/agent + privileged: true + network_mode: "host" + + ruoyi-monitor-admin: + image: ruoyi/ruoyi-monitor-admin:4.5.0 + container_name: rzdata-monitor-admin + environment: + # 时区上海 + TZ: Asia/Shanghai + volumes: + # 配置文件 + - /docker/monitor/logs/:/rzdata/monitor/logs + privileged: true + network_mode: "host" + + ruoyi-xxl-job-admin: + image: ruoyi/ruoyi-xxl-job-admin:4.5.0 + container_name: rzdata-xxl-job-admin + environment: + # 时区上海 + TZ: Asia/Shanghai + volumes: + # 配置文件 + - /docker/xxljob/logs/:/rzdata/xxljob/logs + privileged: true + network_mode: "host" diff --git a/script/docker/nginx/conf/nginx.conf b/script/docker/nginx/conf/nginx.conf new file mode 100644 index 0000000..e9630aa --- /dev/null +++ b/script/docker/nginx/conf/nginx.conf @@ -0,0 +1,111 @@ +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + # 限制body大小 + client_max_body_size 100m; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + upstream server { + ip_hash; + server 127.0.0.1:8080; + server 127.0.0.1:8081; + } + + upstream monitor-admin { + server 127.0.0.1:9090; + } + + upstream xxljob-admin { + server 127.0.0.1:9100; + } + + server { + listen 80; + server_name localhost; + + # https配置参考 start + #listen 443 ssl; + + # 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径 + #ssl on; + #ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改 + #ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改 + #ssl_session_timeout 5m; + #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + #ssl_prefer_server_ciphers on; + # https配置参考 end + + # 演示环境配置 拦截除 GET POST 之外的所有请求 + # if ($request_method !~* GET|POST) { + # rewrite ^/(.*)$ /403; + # } + + # location = /403 { + # default_type application/json; + # return 200 '{"msg":"演示模式,不允许操作","code":500}'; + # } + + # 限制外网访问内网 actuator 相关路径 + location ~ ^(/[^/]*)?/actuator(/.*)?$ { + return 403; + } + + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + index index.html index.htm; + } + + location /prod-api/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://server/; + } + + # https 会拦截内链所有的 http 请求 造成功能无法使用 + # 解决方案1 将 admin 服务 也配置成 https + # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问 + location /admin/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://monitor-admin/admin/; + } + + # https 会拦截内链所有的 http 请求 造成功能无法使用 + # 解决方案1 将 xxljob 服务 也配置成 https + # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问 + location /xxl-job-admin/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://xxljob-admin/xxl-job-admin/; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } +} diff --git a/script/docker/redis/conf/redis.conf b/script/docker/redis/conf/redis.conf new file mode 100644 index 0000000..72255c6 --- /dev/null +++ b/script/docker/redis/conf/redis.conf @@ -0,0 +1,28 @@ +# redis 密码 +requirepass ruoyi123 + +# key 监听器配置 +# notify-keyspace-events Ex + +# 配置持久化文件存储路径 +dir /redis/data +# 配置rdb +# 15分钟内有至少1个key被更改则进行快照 +save 900 1 +# 5分钟内有至少10个key被更改则进行快照 +save 300 10 +# 1分钟内有至少10000个key被更改则进行快照 +save 60 10000 +# 开启压缩 +rdbcompression yes +# rdb文件名 用默认的即可 +dbfilename dump.rdb + +# 开启aof +appendonly yes +# 文件名 +appendfilename "appendonly.aof" +# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢 +# appendfsync always +appendfsync everysec +# appendfsync no diff --git a/script/docker/redis/data/README.md b/script/docker/redis/data/README.md new file mode 100644 index 0000000..fbc5474 --- /dev/null +++ b/script/docker/redis/data/README.md @@ -0,0 +1 @@ +数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据 \ No newline at end of file diff --git a/script/sql/oracle/oracle_ry_vue_4.X.sql b/script/sql/oracle/oracle_ry_vue_4.X.sql new file mode 100644 index 0000000..63d32f7 --- /dev/null +++ b/script/sql/oracle/oracle_ry_vue_4.X.sql @@ -0,0 +1,960 @@ +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +create table sys_dept ( + dept_id number(20) not null, + parent_id number(20) default 0, + ancestors varchar2(500) default '', + dept_name varchar2(30) default '', + order_num number(4) default 0, + leader varchar2(20) default null, + phone varchar2(11) default null, + email varchar2(50) default null, + status char(1) default '0', + del_flag char(1) default '0', + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date +); + +alter table sys_dept add constraint pk_sys_dept primary key (dept_id); + +comment on table sys_dept is '部门表'; +comment on column sys_dept.dept_id is '部门id'; +comment on column sys_dept.parent_id is '父部门id'; +comment on column sys_dept.ancestors is '祖级列表'; +comment on column sys_dept.dept_name is '部门名称'; +comment on column sys_dept.order_num is '显示顺序'; +comment on column sys_dept.leader is '负责人'; +comment on column sys_dept.phone is '联系电话'; +comment on column sys_dept.email is '邮箱'; +comment on column sys_dept.status is '部门状态(0正常 1停用)'; +comment on column sys_dept.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_dept.create_by is '创建者'; +comment on column sys_dept.create_time is '创建时间'; +comment on column sys_dept.update_by is '更新者'; +comment on column sys_dept.update_time is '更新时间'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +create table sys_user ( + user_id number(20) not null, + dept_id number(20) default null, + user_name varchar2(40) not null, + nick_name varchar2(40) not null, + user_type varchar2(10) default 'sys_user', + email varchar2(50) default '', + phonenumber varchar2(11) default '', + sex char(1) default '0', + avatar varchar2(100) default '', + password varchar2(100) default '', + status char(1) default '0', + del_flag char(1) default '0', + login_ip varchar2(128) default '', + login_date date, + create_by varchar2(64), + create_time date, + update_by varchar2(64) default '', + update_time date, + remark varchar2(500) default '' +); + +alter table sys_user add constraint pk_sys_user primary key (user_id); + +comment on table sys_user is '用户信息表'; +comment on column sys_user.user_id is '用户ID'; +comment on column sys_user.dept_id is '部门ID'; +comment on column sys_user.user_name is '用户账号'; +comment on column sys_user.nick_name is '用户昵称'; +comment on column sys_user.user_type is '用户类型(sys_user系统用户)'; +comment on column sys_user.email is '用户邮箱'; +comment on column sys_user.phonenumber is '手机号码'; +comment on column sys_user.sex is '用户性别(0男 1女 2未知)'; +comment on column sys_user.avatar is '头像路径'; +comment on column sys_user.password is '密码'; +comment on column sys_user.status is '帐号状态(0正常 1停用)'; +comment on column sys_user.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_user.login_ip is '最后登录IP'; +comment on column sys_user.login_date is '最后登录时间'; +comment on column sys_user.create_by is '创建者'; +comment on column sys_user.create_time is '创建时间'; +comment on column sys_user.update_by is '更新者'; +comment on column sys_user.update_time is '更新时间'; +comment on column sys_user.remark is '备注'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, '', null, '管理员'); +insert into sys_user values(2, 105, 'lionli', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, '', null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +create table sys_post ( + post_id number(20) not null, + post_code varchar2(64) not null, + post_name varchar2(50) not null, + post_sort number(4) not null, + status char(1) not null, + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date, + remark varchar2(500) +); + +alter table sys_post add constraint pk_sys_post primary key (post_id); + +comment on table sys_post is '岗位信息表'; +comment on column sys_post.post_id is '岗位ID'; +comment on column sys_post.post_code is '岗位编码'; +comment on column sys_post.post_name is '岗位名称'; +comment on column sys_post.post_sort is '显示顺序'; +comment on column sys_post.status is '状态(0正常 1停用)'; +comment on column sys_post.create_by is '创建者'; +comment on column sys_post.create_time is '创建时间'; +comment on column sys_post.update_by is '更新者'; +comment on column sys_post.update_time is '更新时间'; +comment on column sys_post.remark is '备注'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate, '', null, ''); +insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate, '', null, ''); +insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate, '', null, ''); +insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate, '', null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +create table sys_role ( + role_id number(20) not null, + role_name varchar2(30) not null, + role_key varchar2(100) not null, + role_sort number(4) not null, + data_scope char(1) default '1', + menu_check_strictly number(1) default 1, + dept_check_strictly number(1) default 1, + status char(1) not null, + del_flag char(1) default '0', + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date, + remark varchar2(500) default null +); + +alter table sys_role add constraint pk_sys_role primary key (role_id); + +comment on table sys_role is '角色信息表'; +comment on column sys_role.role_id is '角色ID'; +comment on column sys_role.role_name is '角色名称'; +comment on column sys_role.role_key is '角色权限字符串'; +comment on column sys_role.role_sort is '显示顺序'; +comment on column sys_role.data_scope is '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)'; +comment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示'; +comment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示'; +comment on column sys_role.status is '角色状态(0正常 1停用)'; +comment on column sys_role.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_role.create_by is '创建者'; +comment on column sys_role.create_time is '创建时间'; +comment on column sys_role.update_by is '更新者'; +comment on column sys_role.update_time is '更新时间'; +comment on column sys_role.remark is '备注'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate, '', null, '超级管理员'); +insert into sys_role values('2', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate, '', null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +create table sys_menu ( + menu_id number(20) not null, + menu_name varchar2(50) not null, + parent_id number(20) default 0, + order_num number(4) default 0, + path varchar(200) default '', + component varchar(255) default null, + query_param varchar(255) default null, + is_frame number(1) default 1, + is_cache number(1) default 0, + menu_type char(1) default '', + visible char(1) default 0, + status char(1) default 0, + perms varchar2(100) default null, + icon varchar2(100) default '#', + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date , + remark varchar2(500) default '' +); + +alter table sys_menu add constraint pk_sys_menu primary key (menu_id); + +comment on table sys_menu is '菜单权限表'; +comment on column sys_menu.menu_id is '菜单ID'; +comment on column sys_menu.menu_name is '菜单名称'; +comment on column sys_menu.parent_id is '父菜单ID'; +comment on column sys_menu.order_num is '显示顺序'; +comment on column sys_menu.path is '请求地址'; +comment on column sys_menu.component is '路由地址'; +comment on column sys_menu.query_param is '路由参数'; +comment on column sys_menu.is_frame is '是否为外链(0是 1否)'; +comment on column sys_menu.is_cache is '是否缓存(0缓存 1不缓存)'; +comment on column sys_menu.menu_type is '菜单类型(M目录 C菜单 F按钮)'; +comment on column sys_menu.visible is '显示状态(0显示 1隐藏)'; +comment on column sys_menu.status is '菜单状态(0正常 1停用)'; +comment on column sys_menu.perms is '权限标识'; +comment on column sys_menu.icon is '菜单图标'; +comment on column sys_menu.create_by is '创建者'; +comment on column sys_menu.create_time is '创建时间'; +comment on column sys_menu.update_by is '更新者'; +comment on column sys_menu.update_time is '更新时间'; +comment on column sys_menu.remark is '备注'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate, '', null, '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate, '', null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate, '', null, '系统工具目录'); +insert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/JavaLionLi/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate, '', null, 'RuoYi-Vue-Plus官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate, '', null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate, '', null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', sysdate, '', null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', sysdate, '', null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', sysdate, '', null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', sysdate, '', null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', sysdate, '', null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', sysdate, '', null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', sysdate, '', null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', sysdate, '', null, '在线用户菜单'); +insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate, '', null, '缓存列表菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', sysdate, '', null, '缓存监控菜单'); +insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate, '', null, '表单构建菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate, '', null, '代码生成菜单'); +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 'admin', sysdate, '', null, 'Admin监控菜单'); +-- oss菜单 +insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 'admin', sysdate, '', null, '文件管理菜单'); +-- xxl-job-admin控制台 +insert into sys_menu values('120', '任务调度中心', '2', '5', 'XxlJob', 'monitor/xxljob/index', '', 1, 0, 'C', '0', '0', 'monitor:xxljob:list', 'job', 'admin', sysdate, '', null, 'Xxl-Job控制台菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', sysdate, '', null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', sysdate, '', null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate, '', null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate, '', null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate, '', null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate, '', null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate, '', null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate, '', null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate, '', null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate, '', null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate, '', null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate, '', null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate, '', null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate, '', null, ''); +-- oss相关按钮 +insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit', '#', 'admin', sysdate, '', null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +create table sys_user_role ( + user_id number(20) not null, + role_id number(20) not null +); + +alter table sys_user_role add constraint pk_sys_user_role primary key (user_id, role_id); + +comment on table sys_user_role is '用户和角色关联表'; +comment on column sys_user_role.user_id is '用户ID'; +comment on column sys_user_role.role_id is '角色ID'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +create table sys_role_menu ( + role_id number(20) not null, + menu_id number(20) not null +); + +alter table sys_role_menu add constraint pk_sys_role_menu primary key (role_id, menu_id); + +comment on table sys_role_menu is '角色和菜单关联表'; +comment on column sys_role_menu.role_id is '角色ID'; +comment on column sys_role_menu.menu_id is '菜单ID'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +create table sys_role_dept ( + role_id number(20) not null, + dept_id number(20) not null +); + +alter table sys_role_dept add constraint pk_sys_role_dept primary key (role_id, dept_id); + +comment on table sys_role_dept is '角色和部门关联表'; +comment on column sys_role_dept.role_id is '角色ID'; +comment on column sys_role_dept.dept_id is '部门ID'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +create table sys_user_post ( + user_id number(20) not null, + post_id number(20) not null +); + +alter table sys_user_post add constraint pk_sys_user_post primary key (user_id, post_id); + +comment on table sys_user_post is '用户与岗位关联表'; +comment on column sys_user_post.user_id is '用户ID'; +comment on column sys_user_post.post_id is '岗位ID'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +create table sys_oper_log ( + oper_id number(20) not null , + title varchar2(50) default '', + business_type number(2) default 0, + method varchar2(100) default '', + request_method varchar(10) default '', + operator_type number(1) default 0, + oper_name varchar2(50) default '', + dept_name varchar2(50) default '', + oper_url varchar2(255) default '', + oper_ip varchar2(128) default '', + oper_location varchar2(255) default '', + oper_param varchar2(2100) default '', + json_result varchar2(2100) default '', + status number(1) default 0, + error_msg varchar2(2100) default '' , + oper_time date +); + +alter table sys_oper_log add constraint pk_sys_oper_log primary key (oper_id); + +comment on table sys_oper_log is '操作日志记录'; +comment on column sys_oper_log.oper_id is '日志主键'; +comment on column sys_oper_log.title is '模块标题'; +comment on column sys_oper_log.business_type is '业务类型(0其它 1新增 2修改 3删除)'; +comment on column sys_oper_log.method is '方法名称'; +comment on column sys_oper_log.request_method is '请求方式'; +comment on column sys_oper_log.operator_type is '操作类别(0其它 1后台用户 2手机端用户)'; +comment on column sys_oper_log.oper_name is '操作人员'; +comment on column sys_oper_log.dept_name is '部门名称'; +comment on column sys_oper_log.oper_url is '请求URL'; +comment on column sys_oper_log.oper_ip is '主机地址'; +comment on column sys_oper_log.oper_location is '操作地点'; +comment on column sys_oper_log.oper_param is '请求参数'; +comment on column sys_oper_log.json_result is '返回参数'; +comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; +comment on column sys_oper_log.error_msg is '错误消息'; +comment on column sys_oper_log.oper_time is '操作时间'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +create table sys_dict_type ( + dict_id number(20) not null, + dict_name varchar2(100) default '', + dict_type varchar2(100) default '', + status char(1) default '0', + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date, + remark varchar2(500) default null +); + +alter table sys_dict_type add constraint pk_sys_dict_type primary key (dict_id); +create unique index sys_dict_type_index1 on sys_dict_type (dict_type); + +comment on table sys_dict_type is '字典类型表'; +comment on column sys_dict_type.dict_id is '字典主键'; +comment on column sys_dict_type.dict_name is '字典名称'; +comment on column sys_dict_type.dict_type is '字典类型'; +comment on column sys_dict_type.status is '状态(0正常 1停用)'; +comment on column sys_dict_type.create_by is '创建者'; +comment on column sys_dict_type.create_time is '创建时间'; +comment on column sys_dict_type.update_by is '更新者'; +comment on column sys_dict_type.update_time is '更新时间'; +comment on column sys_dict_type.remark is '备注'; + +insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate, '', null, '用户性别列表'); +insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate, '', null, '菜单状态列表'); +insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate, '', null, '系统开关列表'); +insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', sysdate, '', null, '系统是否列表'); +insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', sysdate, '', null, '通知类型列表'); +insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', sysdate, '', null, '通知状态列表'); +insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', sysdate, '', null, '操作类型列表'); +insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', sysdate, '', null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +create table sys_dict_data ( + dict_code number(20) not null, + dict_sort number(4) default 0, + dict_label varchar2(100) default '', + dict_value varchar2(100) default '', + dict_type varchar2(100) default '', + css_class varchar2(100) default null, + list_class varchar2(100) default null, + is_default char(1) default 'N', + status char(1) default '0', + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date, + remark varchar2(500) default null +); + +alter table sys_dict_data add constraint pk_sys_dict_data primary key (dict_code); + +comment on table sys_dict_data is '字典数据表'; +comment on column sys_dict_data.dict_code is '字典主键'; +comment on column sys_dict_data.dict_sort is '字典排序'; +comment on column sys_dict_data.dict_label is '字典标签'; +comment on column sys_dict_data.dict_value is '字典键值'; +comment on column sys_dict_data.dict_type is '字典类型'; +comment on column sys_dict_data.css_class is '样式属性(其他样式扩展)'; +comment on column sys_dict_data.list_class is '表格回显样式'; +comment on column sys_dict_data.is_default is '是否默认(Y是 N否)'; +comment on column sys_dict_data.status is '状态(0正常 1停用)'; +comment on column sys_dict_data.create_by is '创建者'; +comment on column sys_dict_data.create_time is '创建时间'; +comment on column sys_dict_data.update_by is '更新者'; +comment on column sys_dict_data.update_time is '更新时间'; +comment on column sys_dict_data.remark is '备注'; + +insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate, '', null, '性别男'); +insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate, '', null, '性别女'); +insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate, '', null, '性别未知'); +insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate, '', null, '显示菜单'); +insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '隐藏菜单'); +insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate, '', null, '正常状态'); +insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '停用状态'); +insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate, '', null, '系统默认是'); +insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '系统默认否'); +insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate, '', null, '通知'); +insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate, '', null, '公告'); +insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate, '', null, '正常状态'); +insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '关闭状态'); +insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate, '', null, '其他操作'); +insert into sys_dict_data values(18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate, '', null, '新增操作'); +insert into sys_dict_data values(19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate, '', null, '修改操作'); +insert into sys_dict_data values(20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '删除操作'); +insert into sys_dict_data values(21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate, '', null, '授权操作'); +insert into sys_dict_data values(22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate, '', null, '导出操作'); +insert into sys_dict_data values(23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate, '', null, '导入操作'); +insert into sys_dict_data values(24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '强退操作'); +insert into sys_dict_data values(25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate, '', null, '生成操作'); +insert into sys_dict_data values(26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '清空操作'); +insert into sys_dict_data values(27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate, '', null, '正常状态'); +insert into sys_dict_data values(28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +create table sys_config ( + config_id number(20) not null, + config_name varchar2(100) default '', + config_key varchar2(100) default '', + config_value varchar2(100) default '', + config_type char(1) default 'N', + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date, + remark varchar2(500) default null +); +alter table sys_config add constraint pk_sys_config primary key (config_id); + +comment on table sys_config is '参数配置表'; +comment on column sys_config.config_id is '参数主键'; +comment on column sys_config.config_name is '参数名称'; +comment on column sys_config.config_key is '参数键名'; +comment on column sys_config.config_value is '参数键值'; +comment on column sys_config.config_type is '系统内置(Y是 N否)'; +comment on column sys_config.create_by is '创建者'; +comment on column sys_config.create_time is '创建时间'; +comment on column sys_config.update_by is '更新者'; +comment on column sys_config.update_time is '更新时间'; +comment on column sys_config.remark is '备注'; + +insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', sysdate, '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate, '', null, '初始化密码 123456' ); +insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate, '', null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', sysdate, '', null, '是否开启验证码功能(true开启,false关闭)'); +insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate, '', null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(11, 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 'admin', sysdate, '', null, 'true:开启, false:关闭'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +create table sys_logininfor ( + info_id number(20) not null, + user_name varchar2(50) default '', + ipaddr varchar2(128) default '', + login_location varchar2(255) default '', + browser varchar2(50) default '', + os varchar2(50) default '', + status char(1) default '0', + msg varchar2(255) default '', + login_time date +); + +alter table sys_logininfor add constraint pk_sys_logininfor primary key (info_id); + +comment on table sys_logininfor is '系统访问记录'; +comment on column sys_logininfor.info_id is '访问ID'; +comment on column sys_logininfor.user_name is '登录账号'; +comment on column sys_logininfor.ipaddr is '登录IP地址'; +comment on column sys_logininfor.login_location is '登录地点'; +comment on column sys_logininfor.browser is '浏览器类型'; +comment on column sys_logininfor.os is '操作系统'; +comment on column sys_logininfor.status is '登录状态(0成功 1失败)'; +comment on column sys_logininfor.msg is '提示消息'; +comment on column sys_logininfor.login_time is '访问时间'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +create table sys_notice ( + notice_id number(20) not null, + notice_title varchar2(50) not null, + notice_type char(1) not null, + notice_content clob default null, + status char(1) default '0', + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date, + remark varchar2(255) default null +); + +alter table sys_notice add constraint pk_sys_notice primary key (notice_id); + +comment on table sys_notice is '通知公告表'; +comment on column sys_notice.notice_id is '公告主键'; +comment on column sys_notice.notice_title is '公告标题'; +comment on column sys_notice.notice_type is '公告类型(1通知 2公告)'; +comment on column sys_notice.notice_content is '公告内容'; +comment on column sys_notice.status is '公告状态(0正常 1关闭)'; +comment on column sys_notice.create_by is '创建者'; +comment on column sys_notice.create_time is '创建时间'; +comment on column sys_notice.update_by is '更新者'; +comment on column sys_notice.update_time is '更新时间'; +comment on column sys_notice.remark is '备注'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate, '', null, '管理员'); +insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate, '', null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +create table gen_table ( + table_id number(20) not null, + table_name varchar2(200) default '', + table_comment varchar2(500) default '', + sub_table_name varchar(64) default null, + sub_table_fk_name varchar(64) default null, + class_name varchar2(100) default '', + tpl_category varchar2(200) default 'crud', + package_name varchar2(100), + module_name varchar2(30), + business_name varchar2(30), + function_name varchar2(50), + function_author varchar2(50), + gen_type char(1) default '0', + gen_path varchar2(200) default '/', + options varchar2(1000), + create_by varchar2(64) default '', + create_time date, + update_by varchar2(64) default '', + update_time date, + remark varchar2(500) default null +); + +alter table gen_table add constraint pk_gen_table primary key (table_id); + +comment on table gen_table is '代码生成业务表'; +comment on column gen_table.table_id is '编号'; +comment on column gen_table.table_name is '表名称'; +comment on column gen_table.table_comment is '表描述'; +comment on column gen_table.sub_table_name is '关联子表的表名'; +comment on column gen_table.sub_table_fk_name is '子表关联的外键名'; +comment on column gen_table.class_name is '实体类名称'; +comment on column gen_table.tpl_category is '使用的模板(crud单表操作 tree树表操作)'; +comment on column gen_table.package_name is '生成包路径'; +comment on column gen_table.module_name is '生成模块名'; +comment on column gen_table.business_name is '生成业务名'; +comment on column gen_table.function_name is '生成功能名'; +comment on column gen_table.function_author is '生成功能作者'; +comment on column gen_table.gen_type is '生成代码方式(0zip压缩包 1自定义路径)'; +comment on column gen_table.gen_path is '生成路径(不填默认项目路径)'; +comment on column gen_table.options is '其它生成选项'; +comment on column gen_table.create_by is '创建者'; +comment on column gen_table.create_time is '创建时间'; +comment on column gen_table.update_by is '更新者'; +comment on column gen_table.update_time is '更新时间'; +comment on column gen_table.remark is '备注'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +create table gen_table_column ( + column_id number(20) not null, + table_id number(20), + column_name varchar2(200), + column_comment varchar2(500), + column_type varchar2(100), + java_type varchar2(500), + java_field varchar2(200), + is_pk char(1), + is_increment char(1), + is_required char(1), + is_insert char(1), + is_edit char(1), + is_list char(1), + is_query char(1), + query_type varchar(200) default 'EQ', + html_type varchar(200), + dict_type varchar(200) default '', + sort number(4), + create_by varchar(64) default '', + create_time date , + update_by varchar(64) default '', + update_time date +); + +alter table gen_table_column add constraint pk_gen_table_column primary key (column_id); + +comment on table gen_table_column is '代码生成业务表字段'; +comment on column gen_table_column.column_id is '编号'; +comment on column gen_table_column.table_id is '归属表编号'; +comment on column gen_table_column.column_name is '列名称'; +comment on column gen_table_column.column_comment is '列描述'; +comment on column gen_table_column.column_type is '列类型'; +comment on column gen_table_column.java_type is 'JAVA类型'; +comment on column gen_table_column.java_field is 'JAVA字段名'; +comment on column gen_table_column.is_pk is '是否主键(1是)'; +comment on column gen_table_column.is_increment is '是否自增(1是)'; +comment on column gen_table_column.is_required is '是否必填(1是)'; +comment on column gen_table_column.is_insert is '是否为插入字段(1是)'; +comment on column gen_table_column.is_edit is '是否编辑字段(1是)'; +comment on column gen_table_column.is_list is '是否列表字段(1是)'; +comment on column gen_table_column.is_query is '是否查询字段(1是)'; +comment on column gen_table_column.query_type is '查询方式(等于、不等于、大于、小于、范围)'; +comment on column gen_table_column.html_type is '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)'; +comment on column gen_table_column.dict_type is '字典类型'; +comment on column gen_table_column.sort is '排序'; +comment on column gen_table_column.create_by is '创建者'; +comment on column gen_table_column.create_time is '创建时间'; +comment on column gen_table_column.update_by is '更新者'; +comment on column gen_table_column.update_time is '更新时间'; + + +-- ---------------------------- +-- OSS对象存储表 +-- ---------------------------- +create table sys_oss ( + oss_id number(20) not null, + file_name varchar(255) not null, + original_name varchar(255) not null, + file_suffix varchar(10) not null, + url varchar(500) not null, + service varchar(20) default 'minio' not null, + create_by varchar(64) default '', + create_time date, + update_by varchar(64) default '', + update_time date +); + +alter table sys_oss add constraint pk_sys_oss primary key (oss_id); + +comment on table sys_oss is 'OSS对象存储表'; +comment on column sys_oss.oss_id is '对象存储主键'; +comment on column sys_oss.file_name is '文件名'; +comment on column sys_oss.original_name is '原名'; +comment on column sys_oss.file_suffix is '文件后缀名'; +comment on column sys_oss.url is 'URL地址'; +comment on column sys_oss.service is '服务商'; +comment on column sys_oss.create_time is '创建时间'; +comment on column sys_oss.create_by is '上传者'; +comment on column sys_oss.update_time is '更新时间'; +comment on column sys_oss.update_by is '更新者'; + + +-- ---------------------------- +-- OSS对象存储动态配置表 +-- ---------------------------- +create table sys_oss_config ( + oss_config_id number(20) not null, + config_key varchar(20) not null, + access_key varchar(255) default '', + secret_key varchar(255) default '', + bucket_name varchar(255) default '', + prefix varchar(255) default '', + endpoint varchar(255) default '', + domain varchar(255) default '', + is_https char(1) default 'N', + region varchar(255) default '', + access_policy char(1) default '1' not null, + status char(1) default '1', + ext1 varchar(255) default '', + create_by varchar(64) default '', + remark varchar(500) default null, + create_time date, + update_by varchar(64) default '', + update_time date +); + +alter table sys_oss_config add constraint pk_sys_oss_config primary key (oss_config_id); + +comment on table sys_oss_config is '对象存储配置表'; +comment on column sys_oss_config.oss_config_id is '主建'; +comment on column sys_oss_config.config_key is '配置key'; +comment on column sys_oss_config.access_key is 'accesskey'; +comment on column sys_oss_config.secret_key is '秘钥'; +comment on column sys_oss_config.bucket_name is '桶名称'; +comment on column sys_oss_config.prefix is '前缀'; +comment on column sys_oss_config.endpoint is '访问站点'; +comment on column sys_oss_config.domain is '自定义域名'; +comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)'; +comment on column sys_oss_config.region is '域'; +comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)'; +comment on column sys_oss_config.status is '状态(0=正常,1=停用)'; +comment on column sys_oss_config.ext1 is '扩展字段'; +comment on column sys_oss_config.remark is '备注'; +comment on column sys_oss_config.create_by is '创建者'; +comment on column sys_oss_config.create_time is '创建时间'; +comment on column sys_oss_config.update_by is '更新者'; +comment on column sys_oss_config.update_time is '更新时间'; + +insert into sys_oss_config values (1, 'minio', 'rzdata', 'ruoyi123', 'rzdata', '', '127.0.0.1:9000', '','N', '', '1', '0', '', NULL, 'admin', sysdate, 'admin', sysdate); +insert into sys_oss_config values (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate); +insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate); +insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate); +insert into sys_oss_config values (5, 'image', 'rzdata', 'ruoyi123', 'rzdata', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate); + + +-- ---------------------------- +-- 钩子 ,用于session连接之后 自动设置默认的date类型格式化 简化时间查询 +-- 如需设置其它配置 可在此钩子内任意增加处理语句 +-- 例如: SELECT * FROM sys_user WHERE create_time BETWEEN '2022-03-01 00:00:00' AND '2022-04-01 00:00:00' +-- ---------------------------- +create or replace trigger login_trg +after logon on database +begin +execute immediate 'alter session set nls_date_format=''YYYY-MM-DD HH24:MI:SS'''; +end; diff --git a/script/sql/oracle/oracle_test.sql b/script/sql/oracle/oracle_test.sql new file mode 100644 index 0000000..97a427e --- /dev/null +++ b/script/sql/oracle/oracle_test.sql @@ -0,0 +1,196 @@ +create table test_demo ( + id number(20) not null, + dept_id number(20) default null, + user_id number(20) default null, + order_num number(10) default 0, + test_key varchar2(255) default null, + value varchar2(255) default null, + version number(10) default 0, + create_time date, + create_by varchar2(64) default null, + update_time date, + update_by varchar2(64) default null, + del_flag number(2) default 0 +); + +alter table test_demo add constraint pk_test_demo primary key (id); + +comment on table test_demo is '测试单表'; +comment on column test_demo.id is '主键'; +comment on column test_demo.dept_id is '部门id'; +comment on column test_demo.user_id is '用户id'; +comment on column test_demo.order_num is '排序号'; +comment on column test_demo.test_key is 'key键'; +comment on column test_demo.value is '值'; +comment on column test_demo.version is '版本'; +comment on column test_demo.create_time is '创建时间'; +comment on column test_demo.create_by is '创建人'; +comment on column test_demo.update_time is '更新时间'; +comment on column test_demo.update_by is '更新人'; +comment on column test_demo.del_flag is '删除标志'; + +create table test_tree ( + id number(20) not null, + parent_id number(20) default 0, + dept_id number(20) default null, + user_id number(20) default null, + tree_name varchar2(255) default null, + version number(10) default 0, + create_time date, + create_by varchar2(64) default null, + update_time date, + update_by varchar2(64) default null, + del_flag number(2) default 0 +); + +alter table test_tree add constraint pk_test_tree primary key (id); + +comment on table test_tree is '测试树表'; +comment on column test_tree.id is '主键'; +comment on column test_tree.parent_id is '父id'; +comment on column test_tree.dept_id is '部门id'; +comment on column test_tree.user_id is '用户id'; +comment on column test_tree.tree_name is '值'; +comment on column test_tree.version is '版本'; +comment on column test_tree.create_time is '创建时间'; +comment on column test_tree.create_by is '创建人'; +comment on column test_tree.update_time is '更新时间'; +comment on column test_tree.update_by is '更新人'; +comment on column test_tree.del_flag is '删除标志'; + +insert into sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) values (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, 'test', sysdate, null); +insert into sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) values (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, 'test1', sysdate, null); + +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (5, '测试菜单', 0, 5, 'demo', null, 1, 0, 'M', '0', '0', null, 'star', 'admin', sysdate, 'admin', sysdate, ''); + +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', sysdate, '', null, '测试单表菜单'); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', sysdate, '', null, ''); + +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', sysdate, '', null, '测试树表菜单'); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', sysdate, '', null, ''); +insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', sysdate, '', null, ''); + +insert into sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) values (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 'admin', sysdate, '', null, null); +insert into sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) values (4, '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 'admin', sysdate, '', null, null); + +insert into sys_role_menu(role_id, menu_id) values (3, 1); +insert into sys_role_menu(role_id, menu_id) values (3, 5); +insert into sys_role_menu(role_id, menu_id) values (3, 100); +insert into sys_role_menu(role_id, menu_id) values (3, 101); +insert into sys_role_menu(role_id, menu_id) values (3, 102); +insert into sys_role_menu(role_id, menu_id) values (3, 103); +insert into sys_role_menu(role_id, menu_id) values (3, 104); +insert into sys_role_menu(role_id, menu_id) values (3, 105); +insert into sys_role_menu(role_id, menu_id) values (3, 106); +insert into sys_role_menu(role_id, menu_id) values (3, 107); +insert into sys_role_menu(role_id, menu_id) values (3, 108); +insert into sys_role_menu(role_id, menu_id) values (3, 500); +insert into sys_role_menu(role_id, menu_id) values (3, 501); +insert into sys_role_menu(role_id, menu_id) values (3, 1001); +insert into sys_role_menu(role_id, menu_id) values (3, 1002); +insert into sys_role_menu(role_id, menu_id) values (3, 1003); +insert into sys_role_menu(role_id, menu_id) values (3, 1004); +insert into sys_role_menu(role_id, menu_id) values (3, 1005); +insert into sys_role_menu(role_id, menu_id) values (3, 1006); +insert into sys_role_menu(role_id, menu_id) values (3, 1007); +insert into sys_role_menu(role_id, menu_id) values (3, 1008); +insert into sys_role_menu(role_id, menu_id) values (3, 1009); +insert into sys_role_menu(role_id, menu_id) values (3, 1010); +insert into sys_role_menu(role_id, menu_id) values (3, 1011); +insert into sys_role_menu(role_id, menu_id) values (3, 1012); +insert into sys_role_menu(role_id, menu_id) values (3, 1013); +insert into sys_role_menu(role_id, menu_id) values (3, 1014); +insert into sys_role_menu(role_id, menu_id) values (3, 1015); +insert into sys_role_menu(role_id, menu_id) values (3, 1016); +insert into sys_role_menu(role_id, menu_id) values (3, 1017); +insert into sys_role_menu(role_id, menu_id) values (3, 1018); +insert into sys_role_menu(role_id, menu_id) values (3, 1019); +insert into sys_role_menu(role_id, menu_id) values (3, 1020); +insert into sys_role_menu(role_id, menu_id) values (3, 1021); +insert into sys_role_menu(role_id, menu_id) values (3, 1022); +insert into sys_role_menu(role_id, menu_id) values (3, 1023); +insert into sys_role_menu(role_id, menu_id) values (3, 1024); +insert into sys_role_menu(role_id, menu_id) values (3, 1025); +insert into sys_role_menu(role_id, menu_id) values (3, 1026); +insert into sys_role_menu(role_id, menu_id) values (3, 1027); +insert into sys_role_menu(role_id, menu_id) values (3, 1028); +insert into sys_role_menu(role_id, menu_id) values (3, 1029); +insert into sys_role_menu(role_id, menu_id) values (3, 1030); +insert into sys_role_menu(role_id, menu_id) values (3, 1031); +insert into sys_role_menu(role_id, menu_id) values (3, 1032); +insert into sys_role_menu(role_id, menu_id) values (3, 1033); +insert into sys_role_menu(role_id, menu_id) values (3, 1034); +insert into sys_role_menu(role_id, menu_id) values (3, 1035); +insert into sys_role_menu(role_id, menu_id) values (3, 1036); +insert into sys_role_menu(role_id, menu_id) values (3, 1037); +insert into sys_role_menu(role_id, menu_id) values (3, 1038); +insert into sys_role_menu(role_id, menu_id) values (3, 1039); +insert into sys_role_menu(role_id, menu_id) values (3, 1040); +insert into sys_role_menu(role_id, menu_id) values (3, 1041); +insert into sys_role_menu(role_id, menu_id) values (3, 1042); +insert into sys_role_menu(role_id, menu_id) values (3, 1043); +insert into sys_role_menu(role_id, menu_id) values (3, 1044); +insert into sys_role_menu(role_id, menu_id) values (3, 1045); +insert into sys_role_menu(role_id, menu_id) values (3, 1500); +insert into sys_role_menu(role_id, menu_id) values (3, 1501); +insert into sys_role_menu(role_id, menu_id) values (3, 1502); +insert into sys_role_menu(role_id, menu_id) values (3, 1503); +insert into sys_role_menu(role_id, menu_id) values (3, 1504); +insert into sys_role_menu(role_id, menu_id) values (3, 1505); +insert into sys_role_menu(role_id, menu_id) values (3, 1506); +insert into sys_role_menu(role_id, menu_id) values (3, 1507); +insert into sys_role_menu(role_id, menu_id) values (3, 1508); +insert into sys_role_menu(role_id, menu_id) values (3, 1509); +insert into sys_role_menu(role_id, menu_id) values (3, 1510); +insert into sys_role_menu(role_id, menu_id) values (3, 1511); +insert into sys_role_menu(role_id, menu_id) values (4, 5); +insert into sys_role_menu(role_id, menu_id) values (4, 1500); +insert into sys_role_menu(role_id, menu_id) values (4, 1501); +insert into sys_role_menu(role_id, menu_id) values (4, 1502); +insert into sys_role_menu(role_id, menu_id) values (4, 1503); +insert into sys_role_menu(role_id, menu_id) values (4, 1504); +insert into sys_role_menu(role_id, menu_id) values (4, 1505); +insert into sys_role_menu(role_id, menu_id) values (4, 1506); +insert into sys_role_menu(role_id, menu_id) values (4, 1507); +insert into sys_role_menu(role_id, menu_id) values (4, 1508); +insert into sys_role_menu(role_id, menu_id) values (4, 1509); +insert into sys_role_menu(role_id, menu_id) values (4, 1510); +insert into sys_role_menu(role_id, menu_id) values (4, 1511); + +insert into sys_user_role(user_id, role_id) values (3, 3); +insert into sys_user_role(user_id, role_id) values (4, 4); + +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (1, 102, 4, 1, '测试数据权限', '测试', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (2, 102, 3, 2, '子节点1', '111', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (3, 102, 3, 3, '子节点2', '222', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (4, 108, 4, 4, '测试数据', 'demo', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (5, 108, 3, 13, '子节点11', '1111', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (6, 108, 3, 12, '子节点22', '2222', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (7, 108, 3, 11, '子节点33', '3333', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (8, 108, 3, 10, '子节点44', '4444', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (9, 108, 3, 9, '子节点55', '5555', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (10, 108, 3, 8, '子节点66', '6666', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (11, 108, 3, 7, '子节点77', '7777', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (12, 108, 3, 6, '子节点88', '8888', 0, sysdate, 'admin', null, '', 0); +insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (13, 108, 3, 5, '子节点99', '9999', 0, sysdate, 'admin', null, '', 0); + +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (1, 0, 102, 4, '测试数据权限', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (2, 1, 102, 3, '子节点1', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (3, 2, 102, 3, '子节点2', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (4, 0, 108, 4, '测试树1', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (5, 4, 108, 3, '子节点11', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (6, 4, 108, 3, '子节点22', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (7, 4, 108, 3, '子节点33', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (8, 5, 108, 3, '子节点44', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (9, 6, 108, 3, '子节点55', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (10, 7, 108, 3, '子节点66', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (11, 7, 108, 3, '子节点77', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (12, 10, 108, 3, '子节点88', 0, sysdate, 'admin', null, '', 0); +insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (13, 10, 108, 3, '子节点99', 0, sysdate, 'admin', null, '', 0); diff --git a/script/sql/postgres/postgres_ry_vue_4.X.sql b/script/sql/postgres/postgres_ry_vue_4.X.sql new file mode 100644 index 0000000..a27aec5 --- /dev/null +++ b/script/sql/postgres/postgres_ry_vue_4.X.sql @@ -0,0 +1,967 @@ +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +drop table if exists sys_dept; +create table if not exists sys_dept +( + dept_id int8, + parent_id int8 default 0, + ancestors varchar(500)default ''::varchar, + dept_name varchar(30) default ''::varchar, + order_num int4 default 0, + leader varchar(20) default null::varchar, + phone varchar(11) default null::varchar, + email varchar(50) default null::varchar, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + constraint "sys_dept_pk" primary key (dept_id) +); + +comment on table sys_dept is '部门表'; +comment on column sys_dept.dept_id is '部门ID'; +comment on column sys_dept.parent_id is '父部门ID'; +comment on column sys_dept.ancestors is '祖级列表'; +comment on column sys_dept.dept_name is '部门名称'; +comment on column sys_dept.order_num is '显示顺序'; +comment on column sys_dept.leader is '负责人'; +comment on column sys_dept.phone is '联系电话'; +comment on column sys_dept.email is '邮箱'; +comment on column sys_dept.status is '部门状态(0正常 1停用)'; +comment on column sys_dept.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_dept.create_by is '创建者'; +comment on column sys_dept.create_time is '创建时间'; +comment on column sys_dept.update_by is '更新者'; +comment on column sys_dept.update_time is '更新时间'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +drop table if exists sys_user; +create table if not exists sys_user +( + user_id int8, + dept_id int8, + user_name varchar(30) not null, + nick_name varchar(30) not null, + user_type varchar(10) default 'sys_user'::varchar, + email varchar(50) default ''::varchar, + phonenumber varchar(11) default ''::varchar, + sex char default '0'::bpchar, + avatar varchar(100) default ''::varchar, + password varchar(100) default ''::varchar, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + login_ip varchar(128) default ''::varchar, + login_date timestamp, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_user_pk" primary key (user_id) +); + +comment on table sys_user is '用户信息表'; +comment on column sys_user.user_id is '用户ID'; +comment on column sys_user.dept_id is '部门ID'; +comment on column sys_user.user_name is '用户账号'; +comment on column sys_user.nick_name is '用户昵称'; +comment on column sys_user.user_type is '用户类型(sys_user系统用户)'; +comment on column sys_user.email is '用户邮箱'; +comment on column sys_user.phonenumber is '手机号码'; +comment on column sys_user.sex is '用户性别(0男 1女 2未知)'; +comment on column sys_user.avatar is '头像地址'; +comment on column sys_user.password is '密码'; +comment on column sys_user.status is '帐号状态(0正常 1停用)'; +comment on column sys_user.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_user.login_ip is '最后登陆IP'; +comment on column sys_user.login_date is '最后登陆时间'; +comment on column sys_user.create_by is '创建者'; +comment on column sys_user.create_time is '创建时间'; +comment on column sys_user.update_by is '更新者'; +comment on column sys_user.update_time is '更新时间'; +comment on column sys_user.remark is '备注'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 'admin', now(), '', null, '管理员'); +insert into sys_user values(2, 105, 'ry', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 'admin', now(), '', null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +drop table if exists sys_post; +create table if not exists sys_post +( + post_id int8, + post_code varchar(64) not null, + post_name varchar(50) not null, + post_sort int4 not null, + status char not null, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_post_pk" primary key (post_id) +); + +comment on table sys_post is '岗位信息表'; +comment on column sys_post.post_id is '岗位ID'; +comment on column sys_post.post_code is '岗位编码'; +comment on column sys_post.post_name is '岗位名称'; +comment on column sys_post.post_sort is '显示顺序'; +comment on column sys_post.status is '状态(0正常 1停用)'; +comment on column sys_post.create_by is '创建者'; +comment on column sys_post.create_time is '创建时间'; +comment on column sys_post.update_by is '更新者'; +comment on column sys_post.update_time is '更新时间'; +comment on column sys_post.remark is '备注'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', now(), '', null, ''); +insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', now(), '', null, ''); +insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', now(), '', null, ''); +insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', now(), '', null, ''); + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +drop table if exists sys_role; +create table if not exists sys_role +( + role_id int8, + role_name varchar(30) not null, + role_key varchar(100) not null, + role_sort int4 not null, + data_scope char default '1'::bpchar, + menu_check_strictly bool default true, + dept_check_strictly bool default true, + status char not null, + del_flag char default '0'::bpchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_role_pk" primary key (role_id) +); + +comment on table sys_role is '角色信息表'; +comment on column sys_role.role_id is '角色ID'; +comment on column sys_role.role_name is '角色名称'; +comment on column sys_role.role_key is '角色权限字符串'; +comment on column sys_role.role_sort is '显示顺序'; +comment on column sys_role.data_scope is '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)'; +comment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示'; +comment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示'; +comment on column sys_role.status is '角色状态(0正常 1停用)'; +comment on column sys_role.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_role.create_by is '创建者'; +comment on column sys_role.create_time is '创建时间'; +comment on column sys_role.update_by is '更新者'; +comment on column sys_role.update_time is '更新时间'; +comment on column sys_role.remark is '备注'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '超级管理员', 'admin', 1, '1', 't', 't', '0', '0', 'admin', now(), '', null, '超级管理员'); +insert into sys_role values('2', '普通角色', 'common', 2, '2', 't', 't', '0', '0', 'admin', now(), '', null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +drop table if exists sys_menu; +create table if not exists sys_menu +( + menu_id int8, + menu_name varchar(50) not null, + parent_id int8 default 0, + order_num int4 default 0, + path varchar(200) default ''::varchar, + component varchar(255) default null::varchar, + query_param varchar(255) default null::varchar, + is_frame char default '1'::bpchar, + is_cache char default '0'::bpchar, + menu_type char default ''::bpchar, + visible char default '0'::bpchar, + status char default '0'::bpchar, + perms varchar(100) default null::varchar, + icon varchar(100) default '#'::varchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default ''::varchar, + constraint "sys_menu_pk" primary key (menu_id) +); + +comment on table sys_menu is '菜单权限表'; +comment on column sys_menu.menu_id is '菜单ID'; +comment on column sys_menu.menu_name is '菜单名称'; +comment on column sys_menu.parent_id is '父菜单ID'; +comment on column sys_menu.order_num is '显示顺序'; +comment on column sys_menu.path is '路由地址'; +comment on column sys_menu.component is '组件路径'; +comment on column sys_menu.query_param is '路由参数'; +comment on column sys_menu.is_frame is '是否为外链(0是 1否)'; +comment on column sys_menu.is_cache is '是否缓存(0缓存 1不缓存)'; +comment on column sys_menu.menu_type is '菜单类型(M目录 C菜单 F按钮)'; +comment on column sys_menu.visible is '显示状态(0显示 1隐藏)'; +comment on column sys_menu.status is '菜单状态(0正常 1停用)'; +comment on column sys_menu.perms is '权限标识'; +comment on column sys_menu.icon is '菜单图标'; +comment on column sys_menu.create_by is '创建者'; +comment on column sys_menu.create_time is '创建时间'; +comment on column sys_menu.update_by is '更新者'; +comment on column sys_menu.update_time is '更新时间'; +comment on column sys_menu.remark is '备注'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', '1', '0', 'M', '0', '0', '', 'system', 'admin', now(), '', null, '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', '1', '0', 'M', '0', '0', '', 'monitor', 'admin', now(), '', null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', '1', '0', 'M', '0', '0', '', 'tool', 'admin', now(), '', null, '系统工具目录'); +insert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/JavaLionLi/RuoYi-Vue-Plus', null, '', '0', '0', 'M', '0', '0', '', 'guide', 'admin', now(), '', null, 'RuoYi-Vue-Plus官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', '1', '0', 'C', '0', '0', 'system:user:list', 'user', 'admin', now(), '', null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', '1', '0', 'C', '0', '0', 'system:role:list', 'peoples', 'admin', now(), '', null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', '1', '0', 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', now(), '', null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', '1', '0', 'C', '0', '0', 'system:dept:list', 'tree', 'admin', now(), '', null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', '1', '0', 'C', '0', '0', 'system:post:list', 'post', 'admin', now(), '', null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', '1', '0', 'C', '0', '0', 'system:dict:list', 'dict', 'admin', now(), '', null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', '1', '0', 'C', '0', '0', 'system:config:list', 'edit', 'admin', now(), '', null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', '1', '0', 'C', '0', '0', 'system:notice:list', 'message', 'admin', now(), '', null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', '1', '0', 'M', '0', '0', '', 'log', 'admin', now(), '', null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', '1', '0', 'C', '0', '0', 'monitor:online:list', 'online', 'admin', now(), '', null, '在线用户菜单'); +insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', '1', '0', 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', now(), '', null, '缓存列表菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', '1', '0', 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', now(), '', null, '缓存监控菜单'); +insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', '', '1', '0', 'C', '0', '0', 'tool:build:list', 'build', 'admin', now(), '', null, '表单构建菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', '1', '0', 'C', '0', '0', 'tool:gen:list', 'code', 'admin', now(), '', null, '代码生成菜单'); +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', '1', '0', 'C', '0', '0', 'monitor:admin:list', 'dashboard', 'admin', now(), '', null, 'Admin监控菜单'); +-- oss菜单 +insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', '1', '0', 'C', '0', '0', 'system:oss:list', 'upload', 'admin', now(), '', null, '文件管理菜单'); +-- xxl-job-admin控制台 +insert into sys_menu values('120', '任务调度中心', '2', '5', 'XxlJob', 'monitor/xxljob/index', '', '1', '0', 'C', '0', '0', 'monitor:xxljob:list', 'job', 'admin', now(), '', null, 'Xxl-Job控制台菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', '1', '0', 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', now(), '', null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', '1', '0', 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', now(), '', null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:user:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:user:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:user:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:user:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:user:export', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', '1', '0', 'F', '0', '0', 'system:user:import', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', '1', '0', 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', now(), '', null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:role:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:role:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:role:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:role:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:role:export', '#', 'admin', now(), '', null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:remove', '#', 'admin', now(), '', null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:remove', '#', 'admin', now(), '', null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:post:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:post:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:post:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:post:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:post:export', '#', 'admin', now(), '', null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:export', '#', 'admin', now(), '', null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:export', '#', 'admin', now(), '', null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:remove', '#', 'admin', now(), '', null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', now(), '', null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', now(), '', null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', now(), '', null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:edit', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:import', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:preview', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:code', '#', 'admin', now(), '', null, ''); +-- oss相关按钮 +insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:query', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:upload', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:download', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:remove', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:add', '#', 'admin', now(), '', null, ''); +insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:edit', '#', 'admin', now(), '', null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +drop table if exists sys_user_role; +create table if not exists sys_user_role +( + user_id int8 not null, + role_id int8 not null, + constraint sys_user_role_pk primary key (user_id, role_id) +); + +comment on table sys_user_role is '用户和角色关联表'; +comment on column sys_user_role.user_id is '用户ID'; +comment on column sys_user_role.role_id is '角色ID'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +drop table if exists sys_role_menu; +create table if not exists sys_role_menu +( + role_id int8 not null, + menu_id int8 not null, + constraint sys_role_menu_pk primary key (role_id, menu_id) +); + +comment on table sys_role_menu is '角色和菜单关联表'; +comment on column sys_role_menu.role_id is '角色ID'; +comment on column sys_role_menu.menu_id is '菜单ID'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +drop table if exists sys_role_dept; +create table if not exists sys_role_dept +( + role_id int8 not null, + dept_id int8 not null, + constraint sys_role_dept_pk primary key (role_id, dept_id) +); + +comment on table sys_role_dept is '角色和部门关联表'; +comment on column sys_role_dept.role_id is '角色ID'; +comment on column sys_role_dept.dept_id is '部门ID'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +drop table if exists sys_user_post; +create table if not exists sys_user_post +( + user_id int8 not null, + post_id int8 not null, + constraint sys_user_post_pk primary key (user_id, post_id) +); + +comment on table sys_user_post is '用户与岗位关联表'; +comment on column sys_user_post.user_id is '用户ID'; +comment on column sys_user_post.post_id is '岗位ID'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +drop table if exists sys_oper_log; +create table if not exists sys_oper_log +( + oper_id int8, + title varchar(50) default ''::varchar, + business_type int4 default 0, + method varchar(100) default ''::varchar, + request_method varchar(10) default ''::varchar, + operator_type int4 default 0, + oper_name varchar(50) default ''::varchar, + dept_name varchar(50) default ''::varchar, + oper_url varchar(255) default ''::varchar, + oper_ip varchar(128) default ''::varchar, + oper_location varchar(255) default ''::varchar, + oper_param varchar(2000) default ''::varchar, + json_result varchar(2000) default ''::varchar, + status int4 default 0, + error_msg varchar(2000) default ''::varchar, + oper_time timestamp, + constraint sys_oper_log_pk primary key (oper_id) +); + +comment on table sys_oper_log is '操作日志记录'; +comment on column sys_oper_log.oper_id is '日志主键'; +comment on column sys_oper_log.title is '模块标题'; +comment on column sys_oper_log.business_type is '业务类型(0其它 1新增 2修改 3删除)'; +comment on column sys_oper_log.method is '方法名称'; +comment on column sys_oper_log.request_method is '请求方式'; +comment on column sys_oper_log.operator_type is '操作类别(0其它 1后台用户 2手机端用户)'; +comment on column sys_oper_log.oper_name is '操作人员'; +comment on column sys_oper_log.dept_name is '部门名称'; +comment on column sys_oper_log.oper_url is '请求URL'; +comment on column sys_oper_log.oper_ip is '主机地址'; +comment on column sys_oper_log.oper_location is '操作地点'; +comment on column sys_oper_log.oper_param is '请求参数'; +comment on column sys_oper_log.json_result is '返回参数'; +comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; +comment on column sys_oper_log.error_msg is '错误消息'; +comment on column sys_oper_log.oper_time is '操作时间'; + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +drop table if exists sys_dict_type; +create table if not exists sys_dict_type +( + dict_id int8, + dict_name varchar(100) default ''::varchar, + dict_type varchar(100) default ''::varchar, + status char default '0'::bpchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_dict_type_pk primary key (dict_id) +); + +comment on table sys_dict_type is '字典类型表'; +comment on column sys_dict_type.dict_id is '字典主键'; +comment on column sys_dict_type.dict_name is '字典名称'; +comment on column sys_dict_type.dict_type is '字典类型'; +comment on column sys_dict_type.status is '状态(0正常 1停用)'; +comment on column sys_dict_type.create_by is '创建者'; +comment on column sys_dict_type.create_time is '创建时间'; +comment on column sys_dict_type.update_by is '更新者'; +comment on column sys_dict_type.update_time is '更新时间'; +comment on column sys_dict_type.remark is '备注'; + +insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', now(), '', null, '用户性别列表'); +insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', now(), '', null, '菜单状态列表'); +insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', now(), '', null, '系统开关列表'); +insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', now(), '', null, '系统是否列表'); +insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', now(), '', null, '通知类型列表'); +insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', now(), '', null, '通知状态列表'); +insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', now(), '', null, '操作类型列表'); +insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', now(), '', null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +drop table if exists sys_dict_data; +create table if not exists sys_dict_data +( + dict_code int8, + dict_sort int4 default 0, + dict_label varchar(100) default ''::varchar, + dict_value varchar(100) default ''::varchar, + dict_type varchar(100) default ''::varchar, + css_class varchar(100) default null::varchar, + list_class varchar(100) default null::varchar, + is_default char default 'N'::bpchar, + status char default '0'::bpchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_dict_data_pk primary key (dict_code) +); + +comment on table sys_dict_data is '字典数据表'; +comment on column sys_dict_data.dict_code is '字典编码'; +comment on column sys_dict_data.dict_sort is '字典排序'; +comment on column sys_dict_data.dict_label is '字典标签'; +comment on column sys_dict_data.dict_value is '字典键值'; +comment on column sys_dict_data.dict_type is '字典类型'; +comment on column sys_dict_data.css_class is '样式属性(其他样式扩展)'; +comment on column sys_dict_data.list_class is '表格回显样式'; +comment on column sys_dict_data.is_default is '是否默认(Y是 N否)'; +comment on column sys_dict_data.status is '状态(0正常 1停用)'; +comment on column sys_dict_data.create_by is '创建者'; +comment on column sys_dict_data.create_time is '创建时间'; +comment on column sys_dict_data.update_by is '更新者'; +comment on column sys_dict_data.update_time is '更新时间'; +comment on column sys_dict_data.remark is '备注'; + +insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', now(), '', null, '性别男'); +insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', now(), '', null, '性别女'); +insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', now(), '', null, '性别未知'); +insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', now(), '', null, '显示菜单'); +insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', now(), '', null, '隐藏菜单'); +insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', now(), '', null, '正常状态'); +insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', now(), '', null, '停用状态'); +insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', now(), '', null, '系统默认是'); +insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', now(), '', null, '系统默认否'); +insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', now(), '', null, '通知'); +insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', now(), '', null, '公告'); +insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', now(), '', null, '正常状态'); +insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', now(), '', null, '关闭状态'); +insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', now(), '', null, '其他操作'); +insert into sys_dict_data values(18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', now(), '', null, '新增操作'); +insert into sys_dict_data values(19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', now(), '', null, '修改操作'); +insert into sys_dict_data values(20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', now(), '', null, '删除操作'); +insert into sys_dict_data values(21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', now(), '', null, '授权操作'); +insert into sys_dict_data values(22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', now(), '', null, '导出操作'); +insert into sys_dict_data values(23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', now(), '', null, '导入操作'); +insert into sys_dict_data values(24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', now(), '', null, '强退操作'); +insert into sys_dict_data values(25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', now(), '', null, '生成操作'); +insert into sys_dict_data values(26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', now(), '', null, '清空操作'); +insert into sys_dict_data values(27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', now(), '', null, '正常状态'); +insert into sys_dict_data values(28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', now(), '', null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +drop table if exists sys_config; +create table if not exists sys_config +( + config_id int8, + config_name varchar(100) default ''::varchar, + config_key varchar(100) default ''::varchar, + config_value varchar(500) default ''::varchar, + config_type char default 'N'::bpchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_config_pk primary key (config_id) +); + +comment on table sys_config is '参数配置表'; +comment on column sys_config.config_id is '参数主键'; +comment on column sys_config.config_name is '参数名称'; +comment on column sys_config.config_key is '参数键名'; +comment on column sys_config.config_value is '参数键值'; +comment on column sys_config.config_type is '系统内置(Y是 N否)'; +comment on column sys_config.create_by is '创建者'; +comment on column sys_config.create_time is '创建时间'; +comment on column sys_config.update_by is '更新者'; +comment on column sys_config.update_time is '更新时间'; +comment on column sys_config.remark is '备注'; + +insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', now(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', now(), '', null, '初始化密码 123456' ); +insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', now(), '', null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', now(), '', null, '是否开启验证码功能(true开启,false关闭)'); +insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', now(), '', null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(11, 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 'admin', now(), '', null, 'true:开启, false:关闭'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +drop table if exists sys_logininfor; +create table if not exists sys_logininfor +( + info_id int8, + user_name varchar(50) default ''::varchar, + ipaddr varchar(128) default ''::varchar, + login_location varchar(255) default ''::varchar, + browser varchar(50) default ''::varchar, + os varchar(50) default ''::varchar, + status char default '0'::bpchar, + msg varchar(255) default ''::varchar, + login_time timestamp, + constraint sys_logininfor_pk primary key (info_id) +); + +comment on table sys_logininfor is '系统访问记录'; +comment on column sys_logininfor.info_id is '访问ID'; +comment on column sys_logininfor.user_name is '用户账号'; +comment on column sys_logininfor.ipaddr is '登录IP地址'; +comment on column sys_logininfor.login_location is '登录地点'; +comment on column sys_logininfor.browser is '浏览器类型'; +comment on column sys_logininfor.os is '操作系统'; +comment on column sys_logininfor.status is '登录状态(0成功 1失败)'; +comment on column sys_logininfor.msg is '提示消息'; +comment on column sys_logininfor.login_time is '访问时间'; + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +drop table if exists sys_notice; +create table if not exists sys_notice +( + notice_id int8, + notice_title varchar(50) not null, + notice_type char not null, + notice_content text, + status char default '0'::bpchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(255) default null::varchar, + constraint sys_notice_pk primary key (notice_id) +); + +comment on table sys_notice is '通知公告表'; +comment on column sys_notice.notice_id is '公告ID'; +comment on column sys_notice.notice_title is '公告标题'; +comment on column sys_notice.notice_type is '公告类型(1通知 2公告)'; +comment on column sys_notice.notice_content is '公告内容'; +comment on column sys_notice.status is '公告状态(0正常 1关闭)'; +comment on column sys_notice.create_by is '创建者'; +comment on column sys_notice.create_time is '创建时间'; +comment on column sys_notice.update_by is '更新者'; +comment on column sys_notice.update_time is '更新时间'; +comment on column sys_notice.remark is '备注'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', now(), '', null, '管理员'); +insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 'admin', now(), '', null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +drop table if exists gen_table; +create table if not exists gen_table +( + table_id int8, + table_name varchar(200) default ''::varchar, + table_comment varchar(500) default ''::varchar, + sub_table_name varchar(64) default ''::varchar, + sub_table_fk_name varchar(64) default ''::varchar, + class_name varchar(100) default ''::varchar, + tpl_category varchar(200) default 'crud'::varchar, + package_name varchar(100) default null::varchar, + module_name varchar(30) default null::varchar, + business_name varchar(30) default null::varchar, + function_name varchar(50) default null::varchar, + function_author varchar(50) default null::varchar, + gen_type char default '0'::bpchar not null, + gen_path varchar(200) default '/'::varchar, + options varchar(1000) default null::varchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint gen_table_pk primary key (table_id) +); + +comment on table gen_table is '代码生成业务表'; +comment on column gen_table.table_id is '编号'; +comment on column gen_table.table_name is '表名称'; +comment on column gen_table.table_comment is '表描述'; +comment on column gen_table.sub_table_name is '关联子表的表名'; +comment on column gen_table.sub_table_fk_name is '子表关联的外键名'; +comment on column gen_table.class_name is '实体类名称'; +comment on column gen_table.tpl_category is '使用的模板(CRUD单表操作 TREE树表操作)'; +comment on column gen_table.package_name is '生成包路径'; +comment on column gen_table.module_name is '生成模块名'; +comment on column gen_table.business_name is '生成业务名'; +comment on column gen_table.function_name is '生成功能名'; +comment on column gen_table.function_author is '生成功能作者'; +comment on column gen_table.gen_type is '生成代码方式(0zip压缩包 1自定义路径)'; +comment on column gen_table.gen_path is '生成路径(不填默认项目路径)'; +comment on column gen_table.options is '其它生成选项'; +comment on column gen_table.create_by is '创建者'; +comment on column gen_table.create_time is '创建时间'; +comment on column gen_table.update_by is '更新者'; +comment on column gen_table.update_time is '更新时间'; +comment on column gen_table.remark is '备注'; + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +drop table if exists gen_table_column; +create table if not exists gen_table_column +( + column_id int8, + table_id int8, + column_name varchar(200) default null::varchar, + column_comment varchar(500) default null::varchar, + column_type varchar(100) default null::varchar, + java_type varchar(500) default null::varchar, + java_field varchar(200) default null::varchar, + is_pk char default null::bpchar, + is_increment char default null::bpchar, + is_required char default null::bpchar, + is_insert char default null::bpchar, + is_edit char default null::bpchar, + is_list char default null::bpchar, + is_query char default null::bpchar, + query_type varchar(200) default 'EQ'::varchar, + html_type varchar(200) default null::varchar, + dict_type varchar(200) default ''::varchar, + sort int4, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + constraint gen_table_column_pk primary key (column_id) +); + +comment on table gen_table_column is '代码生成业务表字段'; +comment on column gen_table_column.column_id is '编号'; +comment on column gen_table_column.table_id is '归属表编号'; +comment on column gen_table_column.column_name is '列名称'; +comment on column gen_table_column.column_comment is '列描述'; +comment on column gen_table_column.column_type is '列类型'; +comment on column gen_table_column.java_type is 'JAVA类型'; +comment on column gen_table_column.java_field is 'JAVA字段名'; +comment on column gen_table_column.is_pk is '是否主键(1是)'; +comment on column gen_table_column.is_increment is '是否自增(1是)'; +comment on column gen_table_column.is_required is '是否必填(1是)'; +comment on column gen_table_column.is_insert is '是否为插入字段(1是)'; +comment on column gen_table_column.is_edit is '是否编辑字段(1是)'; +comment on column gen_table_column.is_list is '是否列表字段(1是)'; +comment on column gen_table_column.is_query is '是否查询字段(1是)'; +comment on column gen_table_column.query_type is '查询方式(等于、不等于、大于、小于、范围)'; +comment on column gen_table_column.html_type is '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)'; +comment on column gen_table_column.dict_type is '字典类型'; +comment on column gen_table_column.sort is '排序'; +comment on column gen_table_column.create_by is '创建者'; +comment on column gen_table_column.create_time is '创建时间'; +comment on column gen_table_column.update_by is '更新者'; +comment on column gen_table_column.update_time is '更新时间'; + +-- ---------------------------- +-- OSS对象存储表 +-- ---------------------------- +drop table if exists sys_oss; +create table if not exists sys_oss +( + oss_id int8, + file_name varchar(255) default ''::varchar not null, + original_name varchar(255) default ''::varchar not null, + file_suffix varchar(10) default ''::varchar not null, + url varchar(500) default ''::varchar not null, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + service varchar(20) default 'minio'::varchar, + constraint sys_oss_pk primary key (oss_id) +); + +comment on table sys_oss is 'OSS对象存储表'; +comment on column sys_oss.oss_id is '对象存储主键'; +comment on column sys_oss.file_name is '文件名'; +comment on column sys_oss.original_name is '原名'; +comment on column sys_oss.file_suffix is '文件后缀名'; +comment on column sys_oss.url is 'URL地址'; +comment on column sys_oss.create_by is '上传人'; +comment on column sys_oss.create_time is '创建时间'; +comment on column sys_oss.update_by is '更新者'; +comment on column sys_oss.update_time is '更新时间'; +comment on column sys_oss.service is '服务商'; + +-- ---------------------------- +-- OSS对象存储动态配置表 +-- ---------------------------- +drop table if exists sys_oss_config; +create table if not exists sys_oss_config +( + oss_config_id int8, + config_key varchar(20) default ''::varchar not null, + access_key varchar(255) default ''::varchar, + secret_key varchar(255) default ''::varchar, + bucket_name varchar(255) default ''::varchar, + prefix varchar(255) default ''::varchar, + endpoint varchar(255) default ''::varchar, + domain varchar(255) default ''::varchar, + is_https char default 'N'::bpchar, + region varchar(255) default ''::varchar, + access_policy char(1) default '1'::bpchar not null, + status char default '1'::bpchar, + ext1 varchar(255) default ''::varchar, + create_by varchar(64) default ''::varchar, + create_time timestamp, + update_by varchar(64) default ''::varchar, + update_time timestamp, + remark varchar(500) default ''::varchar, + constraint sys_oss_config_pk primary key (oss_config_id) +); + +comment on table sys_oss_config is '对象存储配置表'; +comment on column sys_oss_config.oss_config_id is '主建'; +comment on column sys_oss_config.config_key is '配置key'; +comment on column sys_oss_config.access_key is 'accessKey'; +comment on column sys_oss_config.secret_key is '秘钥'; +comment on column sys_oss_config.bucket_name is '桶名称'; +comment on column sys_oss_config.prefix is '前缀'; +comment on column sys_oss_config.endpoint is '访问站点'; +comment on column sys_oss_config.domain is '自定义域名'; +comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)'; +comment on column sys_oss_config.region is '域'; +comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)'; +comment on column sys_oss_config.status is '状态(0=正常,1=停用)'; +comment on column sys_oss_config.ext1 is '扩展字段'; +comment on column sys_oss_config.create_by is '创建者'; +comment on column sys_oss_config.create_time is '创建时间'; +comment on column sys_oss_config.update_by is '更新者'; +comment on column sys_oss_config.update_time is '更新时间'; +comment on column sys_oss_config.remark is '备注'; + +insert into sys_oss_config values (1, 'minio', 'rzdata', 'ruoyi123', 'rzdata', '', '127.0.0.1:9000', '','N', '', '1', '0', '', 'admin', now(), 'admin', now(), null); +insert into sys_oss_config values (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '1', '', 'admin', now(), 'admin', now(), null); +insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '1', '', 'admin', now(), 'admin', now(), null); +insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', 'admin', now(), 'admin', now(), null); +insert into sys_oss_config values (5, 'image', 'rzdata', 'ruoyi123', 'rzdata', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', 'admin', now(), 'admin', now(), NULL); + +-- 字符串自动转时间 避免框架时间查询报错问题 +create or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$ +select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss'); +$$ language sql strict ; + +create cast (varchar as timestamptz) with function cast_varchar_to_timestamp as IMPLICIT; diff --git a/script/sql/postgres/postgres_test.sql b/script/sql/postgres/postgres_test.sql new file mode 100644 index 0000000..29b4ea7 --- /dev/null +++ b/script/sql/postgres/postgres_test.sql @@ -0,0 +1,196 @@ +DROP TABLE if EXISTS test_demo; +create table if not exists test_demo +( + id int8, + dept_id int8, + user_id int8, + order_num int4 default 0, + test_key varchar(255), + value varchar(255), + version int4 default 0, + create_time timestamp, + create_by varchar(64), + update_time timestamp, + update_by varchar(64), + del_flag int4 default 0 +); + +comment on table test_demo is '测试单表'; +comment on column test_demo.id is '主键'; +comment on column test_demo.dept_id is '部门id'; +comment on column test_demo.user_id is '用户id'; +comment on column test_demo.order_num is '排序号'; +comment on column test_demo.test_key is 'key键'; +comment on column test_demo.value is '值'; +comment on column test_demo.version is '版本'; +comment on column test_demo.create_time is '创建时间'; +comment on column test_demo.create_by is '创建人'; +comment on column test_demo.update_time is '更新时间'; +comment on column test_demo.update_by is '更新人'; +comment on column test_demo.del_flag is '删除标志'; + +DROP TABLE if EXISTS test_tree; +create table if not exists test_tree +( + id int8, + parent_id int8 default 0, + dept_id int8, + user_id int8, + tree_name varchar(255), + version int4 default 0, + create_time timestamp, + create_by varchar(64), + update_time timestamp, + update_by varchar(64), + del_flag integer default 0 +); + +comment on table test_tree is '测试树表'; +comment on column test_tree.id is '主键'; +comment on column test_tree.parent_id is '父id'; +comment on column test_tree.dept_id is '部门id'; +comment on column test_tree.user_id is '用户id'; +comment on column test_tree.tree_name is '值'; +comment on column test_tree.version is '版本'; +comment on column test_tree.create_time is '创建时间'; +comment on column test_tree.create_by is '创建人'; +comment on column test_tree.update_time is '更新时间'; +comment on column test_tree.update_by is '更新人'; +comment on column test_tree.del_flag is '删除标志'; + +INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 'admin', now(), 'test', now(), NULL); +INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 'admin', now(), 'test1', now(), NULL); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 'admin', now(), NULL, NULL, ''); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', now(), '', NULL, '测试单表菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', now(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', now(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', now(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', now(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', now(), '', NULL, ''); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', now(), '', NULL, '测试树表菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', now(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', now(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', now(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', now(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', now(), '', NULL, ''); + +INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (3, '本部门及以下', 'test1', 3, '4', 't', 't', '0', '0', 'admin', now(), 'admin', NULL, NULL); +INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (4, '仅本人', 'test2', 4, '5', 't', 't', '0', '0', 'admin', now(), 'admin', NULL, NULL); + +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511); + +INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3); +INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4); + +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 102, 4, 1, '测试数据权限', '测试', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 102, 3, 2, '子节点1', '111', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 102, 3, 3, '子节点2', '222', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 108, 4, 4, '测试数据', 'demo', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 108, 3, 13, '子节点11', '1111', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 108, 3, 12, '子节点22', '2222', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 108, 3, 11, '子节点33', '3333', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 108, 3, 10, '子节点44', '4444', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 108, 3, 9, '子节点55', '5555', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 108, 3, 8, '子节点66', '6666', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 108, 3, 7, '子节点77', '7777', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 108, 3, 6, '子节点88', '8888', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 108, 3, 5, '子节点99', '9999', 0, now(), 'admin', NULL, NULL, 0); + +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 0, 102, 4, '测试数据权限', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 1, 102, 3, '子节点1', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 2, 102, 3, '子节点2', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 0, 108, 4, '测试树1', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 4, 108, 3, '子节点11', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 4, 108, 3, '子节点22', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 4, 108, 3, '子节点33', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 5, 108, 3, '子节点44', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 6, 108, 3, '子节点55', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 7, 108, 3, '子节点66', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 7, 108, 3, '子节点77', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 10, 108, 3, '子节点88', 0, now(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 10, 108, 3, '子节点99', 0, now(), 'admin', NULL, NULL, 0); diff --git a/script/sql/ry_vue_4.X.sql b/script/sql/ry_vue_4.X.sql new file mode 100644 index 0000000..83810b4 --- /dev/null +++ b/script/sql/ry_vue_4.X.sql @@ -0,0 +1,691 @@ +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +drop table if exists sys_dept; +create table sys_dept ( + dept_id bigint(20) not null comment '部门id', + parent_id bigint(20) default 0 comment '父部门id', + ancestors varchar(500) default '' comment '祖级列表', + dept_name varchar(30) default '' comment '部门名称', + order_num int(4) default 0 comment '显示顺序', + leader varchar(20) default null comment '负责人', + phone varchar(11) default null comment '联系电话', + email varchar(50) default null comment '邮箱', + status char(1) default '0' comment '部门状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (dept_id) +) engine=innodb comment = '部门表'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +drop table if exists sys_user; +create table sys_user ( + user_id bigint(20) not null comment '用户ID', + dept_id bigint(20) default null comment '部门ID', + user_name varchar(30) not null comment '用户账号', + nick_name varchar(30) not null comment '用户昵称', + user_type varchar(10) default 'sys_user' comment '用户类型(sys_user系统用户)', + email varchar(50) default '' comment '用户邮箱', + phonenumber varchar(11) default '' comment '手机号码', + sex char(1) default '0' comment '用户性别(0男 1女 2未知)', + avatar varchar(100) default '' comment '头像地址', + password varchar(100) default '' comment '密码', + status char(1) default '0' comment '帐号状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + login_ip varchar(128) default '' comment '最后登录IP', + login_date datetime comment '最后登录时间', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (user_id) +) engine=innodb comment = '用户信息表'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '管理员'); +insert into sys_user values(2, 105, 'lionli', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +drop table if exists sys_post; +create table sys_post +( + post_id bigint(20) not null comment '岗位ID', + post_code varchar(64) not null comment '岗位编码', + post_name varchar(50) not null comment '岗位名称', + post_sort int(4) not null comment '显示顺序', + status char(1) not null comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (post_id) +) engine=innodb comment = '岗位信息表'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +drop table if exists sys_role; +create table sys_role ( + role_id bigint(20) not null comment '角色ID', + role_name varchar(30) not null comment '角色名称', + role_key varchar(100) not null comment '角色权限字符串', + role_sort int(4) not null comment '显示顺序', + data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + menu_check_strictly tinyint(1) default 1 comment '菜单树选择项是否关联显示', + dept_check_strictly tinyint(1) default 1 comment '部门树选择项是否关联显示', + status char(1) not null comment '角色状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (role_id) +) engine=innodb comment = '角色信息表'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员'); +insert into sys_role values('2', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate(), '', null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +drop table if exists sys_menu; +create table sys_menu ( + menu_id bigint(20) not null comment '菜单ID', + menu_name varchar(50) not null comment '菜单名称', + parent_id bigint(20) default 0 comment '父菜单ID', + order_num int(4) default 0 comment '显示顺序', + path varchar(200) default '' comment '路由地址', + component varchar(255) default null comment '组件路径', + query_param varchar(255) default null comment '路由参数', + is_frame int(1) default 1 comment '是否为外链(0是 1否)', + is_cache int(1) default 0 comment '是否缓存(0缓存 1不缓存)', + menu_type char(1) default '' comment '菜单类型(M目录 C菜单 F按钮)', + visible char(1) default 0 comment '显示状态(0显示 1隐藏)', + status char(1) default 0 comment '菜单状态(0正常 1停用)', + perms varchar(100) default null comment '权限标识', + icon varchar(100) default '#' comment '菜单图标', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注', + primary key (menu_id) +) engine=innodb comment = '菜单权限表'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); +insert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/JavaLionLi/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, 'RuoYi-Vue-Plus官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', sysdate(), '', null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', sysdate(), '', null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', sysdate(), '', null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', sysdate(), '', null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', sysdate(), '', null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', sysdate(), '', null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', sysdate(), '', null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', sysdate(), '', null, '在线用户菜单'); +insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate(), '', null, '缓存列表菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', sysdate(), '', null, '缓存监控菜单'); +insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate(), '', null, '表单构建菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate(), '', null, '代码生成菜单'); +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 'admin', sysdate(), '', null, 'Admin监控菜单'); +-- oss菜单 +insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 'admin', sysdate(), '', null, '文件管理菜单'); +-- xxl-job-admin控制台 +insert into sys_menu values('120', '任务调度中心', '2', '5', 'XxlJob', 'monitor/xxljob/index', '', 1, 0, 'C', '0', '0', 'monitor:xxljob:list', 'job', 'admin', sysdate(), '', null, 'Xxl-Job控制台菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', sysdate(), '', null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', sysdate(), '', null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate(), '', null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate(), '', null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate(), '', null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate(), '', null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate(), '', null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate(), '', null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate(), '', null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate(), '', null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate(), '', null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate(), '', null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate(), '', null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate(), '', null, ''); +-- oss相关按钮 +insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit', '#', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +drop table if exists sys_user_role; +create table sys_user_role ( + user_id bigint(20) not null comment '用户ID', + role_id bigint(20) not null comment '角色ID', + primary key(user_id, role_id) +) engine=innodb comment = '用户和角色关联表'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +drop table if exists sys_role_menu; +create table sys_role_menu ( + role_id bigint(20) not null comment '角色ID', + menu_id bigint(20) not null comment '菜单ID', + primary key(role_id, menu_id) +) engine=innodb comment = '角色和菜单关联表'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +drop table if exists sys_role_dept; +create table sys_role_dept ( + role_id bigint(20) not null comment '角色ID', + dept_id bigint(20) not null comment '部门ID', + primary key(role_id, dept_id) +) engine=innodb comment = '角色和部门关联表'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +drop table if exists sys_user_post; +create table sys_user_post +( + user_id bigint(20) not null comment '用户ID', + post_id bigint(20) not null comment '岗位ID', + primary key (user_id, post_id) +) engine=innodb comment = '用户与岗位关联表'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +drop table if exists sys_oper_log; +create table sys_oper_log ( + oper_id bigint(20) not null comment '日志主键', + title varchar(50) default '' comment '模块标题', + business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)', + method varchar(100) default '' comment '方法名称', + request_method varchar(10) default '' comment '请求方式', + operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', + oper_name varchar(50) default '' comment '操作人员', + dept_name varchar(50) default '' comment '部门名称', + oper_url varchar(255) default '' comment '请求URL', + oper_ip varchar(128) default '' comment '主机地址', + oper_location varchar(255) default '' comment '操作地点', + oper_param varchar(2000) default '' comment '请求参数', + json_result varchar(2000) default '' comment '返回参数', + status int(1) default 0 comment '操作状态(0正常 1异常)', + error_msg varchar(2000) default '' comment '错误消息', + oper_time datetime comment '操作时间', + primary key (oper_id) +) engine=innodb comment = '操作日志记录'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +drop table if exists sys_dict_type; +create table sys_dict_type +( + dict_id bigint(20) not null comment '字典主键', + dict_name varchar(100) default '' comment '字典名称', + dict_type varchar(100) default '' comment '字典类型', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_id), + unique (dict_type) +) engine=innodb comment = '字典类型表'; + +insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate(), '', null, '用户性别列表'); +insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate(), '', null, '菜单状态列表'); +insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate(), '', null, '系统开关列表'); +insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', sysdate(), '', null, '系统是否列表'); +insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', sysdate(), '', null, '通知类型列表'); +insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', sysdate(), '', null, '通知状态列表'); +insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', sysdate(), '', null, '操作类型列表'); +insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', sysdate(), '', null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +drop table if exists sys_dict_data; +create table sys_dict_data +( + dict_code bigint(20) not null comment '字典编码', + dict_sort int(4) default 0 comment '字典排序', + dict_label varchar(100) default '' comment '字典标签', + dict_value varchar(100) default '' comment '字典键值', + dict_type varchar(100) default '' comment '字典类型', + css_class varchar(100) default null comment '样式属性(其他样式扩展)', + list_class varchar(100) default null comment '表格回显样式', + is_default char(1) default 'N' comment '是否默认(Y是 N否)', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_code) +) engine=innodb comment = '字典数据表'; + +insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate(), '', null, '性别男'); +insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别女'); +insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别未知'); +insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '显示菜单'); +insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '隐藏菜单'); +insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '系统默认是'); +insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '系统默认否'); +insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate(), '', null, '通知'); +insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate(), '', null, '公告'); +insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '关闭状态'); +insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作'); +insert into sys_dict_data values(18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '新增操作'); +insert into sys_dict_data values(19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '修改操作'); +insert into sys_dict_data values(20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '删除操作'); +insert into sys_dict_data values(21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作'); +insert into sys_dict_data values(22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作'); +insert into sys_dict_data values(23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作'); +insert into sys_dict_data values(24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '强退操作'); +insert into sys_dict_data values(25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作'); +insert into sys_dict_data values(26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '清空操作'); +insert into sys_dict_data values(27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +drop table if exists sys_config; +create table sys_config ( + config_id bigint(20) not null comment '参数主键', + config_name varchar(100) default '' comment '参数名称', + config_key varchar(100) default '' comment '参数键名', + config_value varchar(500) default '' comment '参数键值', + config_type char(1) default 'N' comment '系统内置(Y是 N否)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (config_id) +) engine=innodb comment = '参数配置表'; + +insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate(), '', null, '初始化密码 123456' ); +insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate(), '', null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启验证码功能(true开启,false关闭)'); +insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(11, 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 'admin', sysdate(), '', null, 'true:开启, false:关闭'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +drop table if exists sys_logininfor; +create table sys_logininfor ( + info_id bigint(20) not null comment '访问ID', + user_name varchar(50) default '' comment '用户账号', + ipaddr varchar(128) default '' comment '登录IP地址', + login_location varchar(255) default '' comment '登录地点', + browser varchar(50) default '' comment '浏览器类型', + os varchar(50) default '' comment '操作系统', + status char(1) default '0' comment '登录状态(0成功 1失败)', + msg varchar(255) default '' comment '提示消息', + login_time datetime comment '访问时间', + primary key (info_id) +) engine=innodb comment = '系统访问记录'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +drop table if exists sys_notice; +create table sys_notice ( + notice_id bigint(20) not null comment '公告ID', + notice_title varchar(50) not null comment '公告标题', + notice_type char(1) not null comment '公告类型(1通知 2公告)', + notice_content longblob default null comment '公告内容', + status char(1) default '0' comment '公告状态(0正常 1关闭)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(255) default null comment '备注', + primary key (notice_id) +) engine=innodb comment = '通知公告表'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员'); +insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate(), '', null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +drop table if exists gen_table; +create table gen_table ( + table_id bigint(20) not null comment '编号', + table_name varchar(200) default '' comment '表名称', + table_comment varchar(500) default '' comment '表描述', + sub_table_name varchar(64) default null comment '关联子表的表名', + sub_table_fk_name varchar(64) default null comment '子表关联的外键名', + class_name varchar(100) default '' comment '实体类名称', + tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作)', + package_name varchar(100) comment '生成包路径', + module_name varchar(30) comment '生成模块名', + business_name varchar(30) comment '生成业务名', + function_name varchar(50) comment '生成功能名', + function_author varchar(50) comment '生成功能作者', + gen_type char(1) default '0' comment '生成代码方式(0zip压缩包 1自定义路径)', + gen_path varchar(200) default '/' comment '生成路径(不填默认项目路径)', + options varchar(1000) comment '其它生成选项', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (table_id) +) engine=innodb comment = '代码生成业务表'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +drop table if exists gen_table_column; +create table gen_table_column ( + column_id bigint(20) not null comment '编号', + table_id bigint(20) comment '归属表编号', + column_name varchar(200) comment '列名称', + column_comment varchar(500) comment '列描述', + column_type varchar(100) comment '列类型', + java_type varchar(500) comment 'JAVA类型', + java_field varchar(200) comment 'JAVA字段名', + is_pk char(1) comment '是否主键(1是)', + is_increment char(1) comment '是否自增(1是)', + is_required char(1) comment '是否必填(1是)', + is_insert char(1) comment '是否为插入字段(1是)', + is_edit char(1) comment '是否编辑字段(1是)', + is_list char(1) comment '是否列表字段(1是)', + is_query char(1) comment '是否查询字段(1是)', + query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)', + html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + dict_type varchar(200) default '' comment '字典类型', + sort int comment '排序', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (column_id) +) engine=innodb comment = '代码生成业务表字段'; + +-- ---------------------------- +-- OSS对象存储表 +-- ---------------------------- +drop table if exists sys_oss; +create table sys_oss ( + oss_id bigint(20) not null comment '对象存储主键', + file_name varchar(255) not null default '' comment '文件名', + original_name varchar(255) not null default '' comment '原名', + file_suffix varchar(10) not null default '' comment '文件后缀名', + url varchar(500) not null comment 'URL地址', + create_time datetime default null comment '创建时间', + create_by varchar(64) default '' comment '上传人', + update_time datetime default null comment '更新时间', + update_by varchar(64) default '' comment '更新人', + service varchar(20) not null default 'minio' comment '服务商', + primary key (oss_id) +) engine=innodb comment ='OSS对象存储表'; + +-- ---------------------------- +-- OSS对象存储动态配置表 +-- ---------------------------- +drop table if exists sys_oss_config; +create table sys_oss_config ( + oss_config_id bigint(20) not null comment '主建', + config_key varchar(20) not null default '' comment '配置key', + access_key varchar(255) default '' comment 'accessKey', + secret_key varchar(255) default '' comment '秘钥', + bucket_name varchar(255) default '' comment '桶名称', + prefix varchar(255) default '' comment '前缀', + endpoint varchar(255) default '' comment '访问站点', + domain varchar(255) default '' comment '自定义域名', + is_https char(1) default 'N' comment '是否https(Y=是,N=否)', + region varchar(255) default '' comment '域', + access_policy char(1) not null default '1' comment '桶权限类型(0=private 1=public 2=custom)', + status char(1) default '1' comment '状态(0=正常,1=停用)', + ext1 varchar(255) default '' comment '扩展字段', + create_by varchar(64) default '' comment '创建者', + create_time datetime default null comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime default null comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (oss_config_id) +) engine=innodb comment='对象存储配置表'; + +insert into sys_oss_config values (1, 'minio', 'rzdata', 'ruoyi123', 'rzdata', '', '127.0.0.1:9000', '','N', '', '1' ,'0', '', 'admin', sysdate(), 'admin', sysdate(), NULL); +insert into sys_oss_config values (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); +insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); +insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'rzdata-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); +insert into sys_oss_config values (5, 'image', 'rzdata', 'ruoyi123', 'rzdata', 'image', '127.0.0.1:9000', '','N', '', '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); diff --git a/script/sql/sqlserver/sqlserver_ry_vue_4.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_4.X.sql new file mode 100644 index 0000000..267f89a --- /dev/null +++ b/script/sql/sqlserver/sqlserver_ry_vue_4.X.sql @@ -0,0 +1,2339 @@ + +CREATE TABLE [gen_table] +( + [table_id] bigint NOT NULL, + [table_name] nvarchar(200) DEFAULT '' NULL, + [table_comment] nvarchar(500) DEFAULT '' NULL, + [sub_table_name] nvarchar(64) NULL, + [sub_table_fk_name] nvarchar(64) NULL, + [class_name] nvarchar(100) DEFAULT '' NULL, + [tpl_category] nvarchar(200) DEFAULT ('crud') NULL, + [package_name] nvarchar(100) NULL, + [module_name] nvarchar(30) NULL, + [business_name] nvarchar(30) NULL, + [function_name] nvarchar(50) NULL, + [function_author] nvarchar(50) NULL, + [gen_type] nchar(1) DEFAULT ('0') NULL, + [gen_path] nvarchar(200) DEFAULT ('/') NULL, + [options] nvarchar(1000) NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) NULL, + CONSTRAINT [PK__gen_tabl__B21E8F2427725F8A] PRIMARY KEY CLUSTERED ([table_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表描述' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_comment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'关联子表的表名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'sub_table_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'子表关联的外键名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'sub_table_fk_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'实体类名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'class_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'使用的模板(crud单表操作 tree树表操作)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'tpl_category' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成包路径' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'package_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成模块名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'module_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成业务名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'business_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成功能名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'function_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成功能作者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'function_author' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成代码方式(0zip压缩包 1自定义路径)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'gen_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成路径(不填默认项目路径)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'gen_path' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'其它生成选项' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'options' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'代码生成业务表' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table' +GO + +CREATE TABLE [gen_table_column] +( + [column_id] bigint NOT NULL, + [table_id] bigint NULL, + [column_name] nvarchar(200) NULL, + [column_comment] nvarchar(500) NULL, + [column_type] nvarchar(100) NULL, + [java_type] nvarchar(500) NULL, + [java_field] nvarchar(200) NULL, + [is_pk] nchar(1) NULL, + [is_increment] nchar(1) NULL, + [is_required] nchar(1) NULL, + [is_insert] nchar(1) NULL, + [is_edit] nchar(1) NULL, + [is_list] nchar(1) NULL, + [is_query] nchar(1) NULL, + [query_type] nvarchar(200) DEFAULT ('EQ') NULL, + [html_type] nvarchar(200) NULL, + [dict_type] nvarchar(200) DEFAULT '' NULL, + [sort] int NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + CONSTRAINT [PK__gen_tabl__E301851F2E68B4E8] PRIMARY KEY CLUSTERED ([column_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'归属表编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'table_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列描述' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_comment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'JAVA类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'java_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'JAVA字段名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'java_field' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否主键(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_pk' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否自增(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_increment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否必填(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_required' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否为插入字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_insert' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否编辑字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_edit' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否列表字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_list' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否查询字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_query' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'查询方式(等于、不等于、大于、小于、范围)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'query_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'html_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'排序' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'代码生成业务表字段' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column' +GO + +CREATE TABLE [sys_config] +( + [config_id] bigint NOT NULL, + [config_name] nvarchar(100) DEFAULT '' NULL, + [config_key] nvarchar(100) DEFAULT '' NULL, + [config_value] nvarchar(500) DEFAULT '' NULL, + [config_type] nchar(1) DEFAULT ('N') NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) NULL, + CONSTRAINT [PK__sys_conf__4AD1BFF182643682] PRIMARY KEY CLUSTERED ([config_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数键名' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数键值' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_value' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'系统内置(Y是 N否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数配置表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config' +GO + +INSERT [sys_config] ([config_id], [config_name], [config_key], [config_value], [config_type], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1, N'主框架页-默认皮肤样式名称', N'sys.index.skinName', N'skin-blue', N'Y', N'admin', getdate(), N'', NULL, N'蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow') +GO +INSERT [sys_config] ([config_id], [config_name], [config_key], [config_value], [config_type], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (2, N'用户管理-账号初始密码', N'sys.user.initPassword', N'123456', N'Y', N'admin', getdate(), N'', NULL, N'初始化密码 123456') +GO +INSERT [sys_config] ([config_id], [config_name], [config_key], [config_value], [config_type], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (3, N'主框架页-侧边栏主题', N'sys.index.sideTheme', N'theme-dark', N'Y', N'admin', getdate(), N'', NULL, N'深色主题theme-dark,浅色主题theme-light') +GO +INSERT [sys_config] ([config_id], [config_name], [config_key], [config_value], [config_type], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (4, N'账号自助-验证码开关', N'sys.account.captchaEnabled', N'true', N'Y', N'admin', getdate(), N'', NULL, N'是否开启验证码功能(true开启,false关闭)') +GO +INSERT [sys_config] ([config_id], [config_name], [config_key], [config_value], [config_type], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (5, N'账号自助-是否开启用户注册功能', N'sys.account.registerUser', N'false', N'Y', N'admin', getdate(), N'', NULL, N'是否开启注册用户功能(true开启,false关闭)') +GO +INSERT [sys_config] ([config_id], [config_name], [config_key], [config_value], [config_type], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (11, N'OSS预览列表资源开关', N'sys.oss.previewListResource', N'true', N'Y', N'admin', getdate(), N'', null, N'true:开启, false:关闭'); +GO + +CREATE TABLE [sys_dept] +( + [dept_id] bigint NOT NULL, + [parent_id] bigint DEFAULT ((0)) NULL, + [ancestors] nvarchar(500)DEFAULT '' NULL, + [dept_name] nvarchar(30) DEFAULT '' NULL, + [order_num] int DEFAULT ((0)) NULL, + [leader] nvarchar(20) NULL, + [phone] nvarchar(11) NULL, + [email] nvarchar(50) NULL, + [status] nchar(1) DEFAULT ('0') NULL, + [del_flag] nchar(1) DEFAULT ('0') NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + CONSTRAINT [PK__sys_dept__DCA659747DE13804] PRIMARY KEY CLUSTERED ([dept_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'父部门id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'parent_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'祖级列表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'ancestors' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'dept_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'order_num' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'负责人' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'leader' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'联系电话' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'phone' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'邮箱' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'email' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept' +GO + +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (100, 0, N'0', N'若依科技', 0, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (101, 100, N'0,100', N'深圳总公司', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (102, 100, N'0,100', N'长沙分公司', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (103, 101, N'0,100,101', N'研发部门', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (104, 101, N'0,100,101', N'市场部门', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (105, 101, N'0,100,101', N'测试部门', 3, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (106, 101, N'0,100,101', N'财务部门', 4, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (107, 101, N'0,100,101', N'运维部门', 5, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (108, 102, N'0,100,102', N'市场部门', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO +INSERT [sys_dept] ([dept_id], [parent_id], [ancestors], [dept_name], [order_num], [leader], [phone], [email], [status], [del_flag], [create_by], [create_time], [update_by], [update_time]) VALUES (109, 102, N'0,100,102', N'财务部门', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) +GO + +CREATE TABLE [sys_dict_data] +( + [dict_code] bigint NOT NULL, + [dict_sort] int DEFAULT ((0)) NULL, + [dict_label] nvarchar(100) DEFAULT '' NULL, + [dict_value] nvarchar(100) DEFAULT '' NULL, + [dict_type] nvarchar(100) DEFAULT '' NULL, + [css_class] nvarchar(100) NULL, + [list_class] nvarchar(100) NULL, + [is_default] nchar(1) DEFAULT ('N') NULL, + [status] nchar(1) DEFAULT ('0') NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) NULL, + CONSTRAINT [PK__sys_dict__19CBC34B661AF3B3] PRIMARY KEY CLUSTERED ([dict_code]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典排序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典标签' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_label' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典键值' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_value' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'样式属性(其他样式扩展)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'css_class' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表格回显样式' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'list_class' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否默认(Y是 N否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'is_default' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典数据表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data' +GO + +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1, 1, N'男', N'0', N'sys_user_sex', N'', N'', N'Y', N'0', N'admin', getdate(), N'', NULL, N'性别男') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (2, 2, N'女', N'1', N'sys_user_sex', N'', N'', N'N', N'0', N'admin', getdate(), N'', NULL, N'性别女') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (3, 3, N'未知', N'2', N'sys_user_sex', N'', N'', N'N', N'0', N'admin', getdate(), N'', NULL, N'性别未知') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (4, 1, N'显示', N'0', N'sys_show_hide', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'显示菜单') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (5, 2, N'隐藏', N'1', N'sys_show_hide', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'隐藏菜单') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (6, 1, N'正常', N'0', N'sys_normal_disable', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'正常状态') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (7, 2, N'停用', N'1', N'sys_normal_disable', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'停用状态') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (8, 1, N'正常', N'0', N'sys_job_status', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'正常状态') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (9, 2, N'暂停', N'1', N'sys_job_status', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'停用状态') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (10, 1, N'默认', N'DEFAULT', N'sys_job_group', N'', N'', N'Y', N'0', N'admin', getdate(), N'', NULL, N'默认分组') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (11, 2, N'系统', N'SYSTEM', N'sys_job_group', N'', N'', N'N', N'0', N'admin', getdate(), N'', NULL, N'系统分组') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (12, 1, N'是', N'Y', N'sys_yes_no', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'系统默认是') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (13, 2, N'否', N'N', N'sys_yes_no', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'系统默认否') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (14, 1, N'通知', N'1', N'sys_notice_type', N'', N'warning', N'Y', N'0', N'admin', getdate(), N'', NULL, N'通知') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (15, 2, N'公告', N'2', N'sys_notice_type', N'', N'success', N'N', N'0', N'admin', getdate(), N'', NULL, N'公告') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (16, 1, N'正常', N'0', N'sys_notice_status', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'正常状态') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (17, 2, N'关闭', N'1', N'sys_notice_status', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'关闭状态') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (29, 99, N'其他', N'0', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'其他操作'); +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (18, 1, N'新增', N'1', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'新增操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (19, 2, N'修改', N'2', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'修改操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (20, 3, N'删除', N'3', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'删除操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (21, 4, N'授权', N'4', N'sys_oper_type', N'', N'primary', N'N', N'0', N'admin', getdate(), N'', NULL, N'授权操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (22, 5, N'导出', N'5', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'导出操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (23, 6, N'导入', N'6', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'导入操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (24, 7, N'强退', N'7', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'强退操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (25, 8, N'生成代码', N'8', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'生成操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (26, 9, N'清空数据', N'9', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'清空操作') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (27, 1, N'成功', N'0', N'sys_common_status', N'', N'primary', N'N', N'0', N'admin', getdate(), N'', NULL, N'正常状态') +GO +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (28, 2, N'失败', N'1', N'sys_common_status', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'停用状态') +GO + +CREATE TABLE [sys_dict_type] +( + [dict_id] bigint NOT NULL, + [dict_name] nvarchar(100) DEFAULT '' NULL, + [dict_type] nvarchar(100) DEFAULT '' NULL, + [status] nchar(1) DEFAULT ('0') NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) NULL, + CONSTRAINT [PK__sys_dict__3BD4186C409C5391] PRIMARY KEY CLUSTERED ([dict_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type' +GO + +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1, N'用户性别', N'sys_user_sex', N'0', N'admin', getdate(), N'', NULL, N'用户性别列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (2, N'菜单状态', N'sys_show_hide', N'0', N'admin', getdate(), N'', NULL, N'菜单状态列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (3, N'系统开关', N'sys_normal_disable', N'0', N'admin', getdate(), N'', NULL, N'系统开关列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (4, N'任务状态', N'sys_job_status', N'0', N'admin', getdate(), N'', NULL, N'任务状态列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (5, N'任务分组', N'sys_job_group', N'0', N'admin', getdate(), N'', NULL, N'任务分组列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (6, N'系统是否', N'sys_yes_no', N'0', N'admin', getdate(), N'', NULL, N'系统是否列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (7, N'通知类型', N'sys_notice_type', N'0', N'admin', getdate(), N'', NULL, N'通知类型列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (8, N'通知状态', N'sys_notice_status', N'0', N'admin', getdate(), N'', NULL, N'通知状态列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (9, N'操作类型', N'sys_oper_type', N'0', N'admin', getdate(), N'', NULL, N'操作类型列表') +GO +INSERT [sys_dict_type] ([dict_id], [dict_name], [dict_type], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (10, N'系统状态', N'sys_common_status', N'0', N'admin', getdate(), N'', NULL, N'登录状态列表') +GO + +CREATE TABLE [sys_logininfor] +( + [info_id] bigint NOT NULL, + [user_name] nvarchar(50) DEFAULT '' NULL, + [ipaddr] nvarchar(128) DEFAULT '' NULL, + [login_location] nvarchar(255) DEFAULT '' NULL, + [browser] nvarchar(50) DEFAULT '' NULL, + [os] nvarchar(50) DEFAULT '' NULL, + [status] nchar(1) DEFAULT ('0') NULL, + [msg] nvarchar(255) DEFAULT '' NULL, + [login_time] datetime2(7) NULL, + CONSTRAINT [PK__sys_logi__3D8A9C1A1854AE10] PRIMARY KEY CLUSTERED ([info_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'访问ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'info_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户账号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录IP地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'ipaddr' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录地点' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'login_location' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'浏览器类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'browser' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作系统' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'os' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录状态(0成功 1失败)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'提示消息' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'msg' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'访问时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'login_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'系统访问记录' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor' +GO + +CREATE TABLE [sys_menu] +( + [menu_id] bigint NOT NULL, + [menu_name] nvarchar(50) NOT NULL, + [parent_id] bigint DEFAULT ((0)) NULL, + [order_num] int DEFAULT ((0)) NULL, + [path] nvarchar(200) DEFAULT '' NULL, + [component] nvarchar(255) NULL, + [query_param] nvarchar(255) NULL, + [is_frame] int DEFAULT ((1)) NULL, + [is_cache] int DEFAULT ((0)) NULL, + [menu_type] nchar(1) DEFAULT '' NULL, + [visible] nchar(1) DEFAULT ((0)) NULL, + [status] nchar(1) DEFAULT ((0)) NULL, + [perms] nvarchar(100) NULL, + [icon] nvarchar(100) DEFAULT ('#') NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) DEFAULT '' NULL, + CONSTRAINT [PK__sys_menu__4CA0FADCF8545C58] PRIMARY KEY CLUSTERED ([menu_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'父菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'parent_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'order_num' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'路由地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'path' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'组件路径' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'component' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'路由参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'query_param' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否为外链(0是 1否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'is_frame' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否缓存(0缓存 1不缓存)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'is_cache' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单类型(M目录 C菜单 F按钮)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示状态(0显示 1隐藏)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'visible' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'权限标识' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'perms' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单图标' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'icon' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单权限表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu' +GO + +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1, N'系统管理', 0, 1, N'system', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'system', N'admin', getdate(), N'', NULL, N'系统管理目录') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (2, N'系统监控', 0, 2, N'monitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', N'admin', getdate(), N'', NULL, N'系统监控目录') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (3, N'系统工具', 0, 3, N'tool', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'tool', N'admin', getdate(), N'', NULL, N'系统工具目录') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (100, N'用户管理', 1, 1, N'user', N'system/user/index', N'', 1, 0, N'C', N'0', N'0', N'system:user:list', N'user', N'admin', getdate(), N'', NULL, N'用户管理菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (101, N'角色管理', 1, 2, N'role', N'system/role/index', N'', 1, 0, N'C', N'0', N'0', N'system:role:list', N'peoples', N'admin', getdate(), N'', NULL, N'角色管理菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (102, N'菜单管理', 1, 3, N'menu', N'system/menu/index', N'', 1, 0, N'C', N'0', N'0', N'system:menu:list', N'tree-table', N'admin', getdate(), N'', NULL, N'菜单管理菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (103, N'部门管理', 1, 4, N'dept', N'system/dept/index', N'', 1, 0, N'C', N'0', N'0', N'system:dept:list', N'tree', N'admin', getdate(), N'', NULL, N'部门管理菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (104, N'岗位管理', 1, 5, N'post', N'system/post/index', N'', 1, 0, N'C', N'0', N'0', N'system:post:list', N'post', N'admin', getdate(), N'', NULL, N'岗位管理菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (105, N'字典管理', 1, 6, N'dict', N'system/dict/index', N'', 1, 0, N'C', N'0', N'0', N'system:dict:list', N'dict', N'admin', getdate(), N'', NULL, N'字典管理菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (106, N'参数设置', 1, 7, N'config', N'system/config/index', N'', 1, 0, N'C', N'0', N'0', N'system:config:list', N'edit', N'admin', getdate(), N'', NULL, N'参数设置菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (107, N'通知公告', 1, 8, N'notice', N'system/notice/index', N'', 1, 0, N'C', N'0', N'0', N'system:notice:list', N'message', N'admin', getdate(), N'', NULL, N'通知公告菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (108, N'日志管理', 1, 9, N'log', N'', N'', 1, 0, N'M', N'0', N'0', N'', N'log', N'admin', getdate(), N'', NULL, N'日志管理菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (109, N'在线用户', 2, 1, N'online', N'monitor/online/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:online:list', N'online', N'admin', getdate(), N'', NULL, N'在线用户菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (112, N'缓存列表', 2, 6, N'cacheList', N'monitor/cache/list', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis-list', N'admin', getdate(), N'', NULL, N'缓存列表菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (113, N'缓存监控', 2, 5, N'cache', N'monitor/cache/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis', N'admin', getdate(), N'', NULL, N'缓存监控菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (114, N'表单构建', 3, 1, N'build', N'tool/build/index', N'', 1, 0, N'C', N'0', N'0', N'tool:build:list', N'build', N'admin', getdate(), N'', NULL, N'表单构建菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (115, N'代码生成', 3, 2, N'gen', N'tool/gen/index', N'', 1, 0, N'C', N'0', N'0', N'tool:gen:list', N'code', N'admin', getdate(), N'', NULL, N'代码生成菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (117, N'Admin监控', 2, 5, N'Admin', N'monitor/admin/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:admin:list', N'dashboard', N'admin', getdate(), N'', NULL, N'Admin监控菜单'); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (118, N'文件管理', 1, 10, N'oss', N'system/oss/index', N'', 1, 0, N'C', '0', N'0', N'system:oss:list', N'upload', N'admin', getdate(), N'', NULL, N'文件管理菜单'); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (120, N'任务调度中心', 2, 5, N'XxlJob', N'monitor/xxljob/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:xxljob:list', N'job', N'admin', getdate(), N'', NULL, N'Xxl-Job控制台菜单'); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (500, N'操作日志', 108, 1, N'operlog', N'monitor/operlog/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:operlog:list', N'form', N'admin', getdate(), N'', NULL, N'操作日志菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (501, N'登录日志', 108, 2, N'logininfor', N'monitor/logininfor/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:logininfor:list', N'logininfor', N'admin', getdate(), N'', NULL, N'登录日志菜单') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1001, N'用户查询', 100, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1002, N'用户新增', 100, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:add', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1003, N'用户修改', 100, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1004, N'用户删除', 100, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1005, N'用户导出', 100, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:export', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1006, N'用户导入', 100, 6, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:import', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1007, N'重置密码', 100, 7, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:resetPwd', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1008, N'角色查询', 101, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1009, N'角色新增', 101, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:add', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1010, N'角色修改', 101, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1011, N'角色删除', 101, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1012, N'角色导出', 101, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:export', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1013, N'菜单查询', 102, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1014, N'菜单新增', 102, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:add', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1015, N'菜单修改', 102, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1016, N'菜单删除', 102, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1017, N'部门查询', 103, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1018, N'部门新增', 103, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:add', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1019, N'部门修改', 103, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1020, N'部门删除', 103, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1021, N'岗位查询', 104, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1022, N'岗位新增', 104, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:add', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1023, N'岗位修改', 104, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1024, N'岗位删除', 104, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1025, N'岗位导出', 104, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:export', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1026, N'字典查询', 105, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1027, N'字典新增', 105, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:add', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1028, N'字典修改', 105, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1029, N'字典删除', 105, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1030, N'字典导出', 105, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:export', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1031, N'参数查询', 106, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1032, N'参数新增', 106, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:add', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1033, N'参数修改', 106, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1034, N'参数删除', 106, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1035, N'参数导出', 106, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:export', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1036, N'公告查询', 107, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1037, N'公告新增', 107, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:add', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1038, N'公告修改', 107, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1039, N'公告删除', 107, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1040, N'操作查询', 500, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1041, N'操作删除', 500, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1042, N'日志导出', 500, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:export', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1043, N'登录查询', 501, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1044, N'登录删除', 501, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1045, N'日志导出', 501, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:export', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', N'admin', getdate(), N'', null, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1046, N'在线查询', 109, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1047, N'批量强退', 109, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:batchLogout', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1048, N'单条强退', 109, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:forceLogout', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1055, N'生成查询', 115, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:query', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1056, N'生成修改', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:edit', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1057, N'生成删除', 115, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:remove', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1058, N'导入代码', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:import', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1059, N'预览代码', 115, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:preview', N'#', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1060, N'生成代码', 115, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:code', N'#', N'admin', getdate(), N'', NULL, N'') +GO +-- oss相关按钮 +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1600, N'文件查询', 118, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:query', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1601, N'文件上传', 118, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:upload', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1602, N'文件下载', 118, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:download', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1603, N'文件删除', 118, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:remove', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1604, N'配置添加', 118, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:add', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1605, N'配置编辑', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:edit', N'#', N'admin', getdate(), N'', NULL, N''); +GO + +CREATE TABLE [sys_notice] +( + [notice_id] bigint NOT NULL, + [notice_title] nvarchar(50) NOT NULL, + [notice_type] nchar(1) NOT NULL, + [notice_content] nvarchar(max) NULL, + [status] nchar(1) DEFAULT ('0') NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(255) NULL, + CONSTRAINT [PK__sys_noti__3E82A5DB0EC94801] PRIMARY KEY CLUSTERED ([notice_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +TEXTIMAGE_ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告标题' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_title' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告类型(1通知 2公告)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告内容' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_content' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告状态(0正常 1关闭)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'通知公告表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice' +GO + +INSERT [sys_notice] ([notice_id], [notice_title], [notice_type], [notice_content], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1, N'温馨提醒:2018-07-01 若依新版本发布啦', N'2', N'

1111111111

', N'0', N'admin', getdate(), N'admin', getdate(), N'管理员') +GO +INSERT [sys_notice] ([notice_id], [notice_title], [notice_type], [notice_content], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (2, N'维护通知:2018-07-01 若依系统凌晨维护', N'1', N'

', N'0', N'admin', getdate(), N'admin', getdate(), N'管理员') +GO + +CREATE TABLE [sys_oper_log] +( + [oper_id] bigint NOT NULL, + [title] nvarchar(50) DEFAULT '' NULL, + [business_type] int DEFAULT ((0)) NULL, + [method] nvarchar(100) DEFAULT '' NULL, + [request_method] nvarchar(10) DEFAULT '' NULL, + [operator_type] int DEFAULT ((0)) NULL, + [oper_name] nvarchar(50) DEFAULT '' NULL, + [dept_name] nvarchar(50) DEFAULT '' NULL, + [oper_url] nvarchar(255) DEFAULT '' NULL, + [oper_ip] nvarchar(128) DEFAULT '' NULL, + [oper_location] nvarchar(255) DEFAULT '' NULL, + [oper_param] nvarchar(2000) DEFAULT '' NULL, + [json_result] nvarchar(2000) DEFAULT '' NULL, + [status] int DEFAULT ((0)) NULL, + [error_msg] nvarchar(2000) DEFAULT '' NULL, + [oper_time] datetime2(7) NULL, + CONSTRAINT [PK__sys_oper__34723BF9BD954573] PRIMARY KEY CLUSTERED ([oper_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'日志主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'模块标题' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'title' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'业务类型(0其它 1新增 2修改 3删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'business_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'方法名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'method' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求方式' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'request_method' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作类别(0其它 1后台用户 2手机端用户)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'operator_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作人员' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'dept_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求URL' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_url' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'主机地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_ip' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作地点' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_location' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_param' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'返回参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'json_result' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作状态(0正常 1异常)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'错误消息' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'error_msg' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作日志记录' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log' +GO + +CREATE TABLE [sys_post] +( + [post_id] bigint NOT NULL, + [post_code] nvarchar(64) NOT NULL, + [post_name] nvarchar(50) NOT NULL, + [post_sort] int NOT NULL, + [status] nchar(1) NOT NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) NULL, + CONSTRAINT [PK__sys_post__3ED7876668E2D081] PRIMARY KEY CLUSTERED ([post_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post' +GO + +INSERT [sys_post] ([post_id], [post_code], [post_name], [post_sort], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1, N'ceo', N'董事长', 1, N'0', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_post] ([post_id], [post_code], [post_name], [post_sort], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (2, N'se', N'项目经理', 2, N'0', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_post] ([post_id], [post_code], [post_name], [post_sort], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (3, N'hr', N'人力资源', 3, N'0', N'admin', getdate(), N'', NULL, N'') +GO +INSERT [sys_post] ([post_id], [post_code], [post_name], [post_sort], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (4, N'user', N'普通员工', 4, N'0', N'admin', getdate(), N'', NULL, N'') +GO + +CREATE TABLE [sys_role] +( + [role_id] bigint NOT NULL, + [role_name] nvarchar(30) NOT NULL, + [role_key] nvarchar(100) NOT NULL, + [role_sort] int NOT NULL, + [data_scope] nchar(1) DEFAULT ('1') NULL, + [menu_check_strictly] tinyint DEFAULT ((1)) NULL, + [dept_check_strictly] tinyint DEFAULT ((1)) NULL, + [status] nchar(1) NOT NULL, + [del_flag] nchar(1) DEFAULT ('0') NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) NULL, + CONSTRAINT [PK__sys_role__760965CCF9383145] PRIMARY KEY CLUSTERED ([role_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色权限字符串' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'data_scope' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单树选择项是否关联显示' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'menu_check_strictly' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门树选择项是否关联显示' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'dept_check_strictly' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role' +GO + +INSERT [sys_role] ([role_id], [role_name], [role_key], [role_sort], [data_scope], [menu_check_strictly], [dept_check_strictly], [status], [del_flag], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1, N'超级管理员', N'admin', 1, N'1', 1, 1, N'0', N'0', N'admin', getdate(), N'', NULL, N'超级管理员') +GO +INSERT [sys_role] ([role_id], [role_name], [role_key], [role_sort], [data_scope], [menu_check_strictly], [dept_check_strictly], [status], [del_flag], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (2, N'普通角色', N'common', 2, N'2', 1, 1, N'0', N'0', N'admin', getdate(), N'admin', CAST(N'2021-12-04T15:44:20.0000000' AS DateTime2), N'普通角色') +GO + +CREATE TABLE [sys_role_dept] +( + [role_id] bigint NOT NULL, + [dept_id] bigint NOT NULL, + CONSTRAINT [PK__sys_role__2BC3005BABBCA08A] PRIMARY KEY CLUSTERED ([role_id], [dept_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色和部门关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept' +GO + +INSERT [sys_role_dept] ([role_id], [dept_id]) VALUES (2, 100) +GO +INSERT [sys_role_dept] ([role_id], [dept_id]) VALUES (2, 101) +GO +INSERT [sys_role_dept] ([role_id], [dept_id]) VALUES (2, 105) +GO + +CREATE TABLE [sys_role_menu] +( + [role_id] bigint NOT NULL, + [menu_id] bigint NOT NULL, + CONSTRAINT [PK__sys_role__A2C36A6187BA4B17] PRIMARY KEY CLUSTERED ([role_id], [menu_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu', + 'COLUMN', N'menu_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色和菜单关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu' +GO + +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 2) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 3) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 100) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 101) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 102) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 103) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 104) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 105) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 106) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 107) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 108) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 109) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 110) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 111) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 112) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 113) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 114) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 115) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 116) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 500) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 501) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1001) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1002) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1003) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1004) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1005) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1006) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1007) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1008) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1009) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1010) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1011) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1012) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1013) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1014) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1015) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1016) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1017) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1018) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1019) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1020) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1021) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1022) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1023) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1024) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1025) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1026) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1027) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1028) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1029) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1030) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1031) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1032) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1033) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1034) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1035) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1036) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1037) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1038) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1039) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1040) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1041) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1042) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1043) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1044) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1045) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1046) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1047) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1048) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1049) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1050) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1051) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1052) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1053) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1054) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1055) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1056) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1057) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1058) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1059) +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1060) +GO + +CREATE TABLE [sys_user] +( + [user_id] bigint NOT NULL, + [dept_id] bigint NULL, + [user_name] nvarchar(30) NOT NULL, + [nick_name] nvarchar(30) NOT NULL, + [user_type] nvarchar(10) DEFAULT ('sys_user') NULL, + [email] nvarchar(50) DEFAULT '' NULL, + [phonenumber] nvarchar(11) DEFAULT '' NULL, + [sex] nchar(1) DEFAULT ('0') NULL, + [avatar] nvarchar(100) DEFAULT '' NULL, + [password] nvarchar(100) DEFAULT '' NULL, + [status] nchar(1) DEFAULT ('0') NULL, + [del_flag] nchar(1) DEFAULT ('0') NULL, + [login_ip] nvarchar(128) DEFAULT '' NULL, + [login_date] datetime2(7) NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) NULL, + CONSTRAINT [PK__sys_user__B9BE370F79170B6A] PRIMARY KEY CLUSTERED ([user_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户账号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户昵称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'nick_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户类型(sys_user系统用户)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户邮箱' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'email' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'手机号码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'phonenumber' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户性别(0男 1女 2未知)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'sex' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'头像地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'avatar' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'密码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'password' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'帐号状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'最后登录IP' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'login_ip' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'最后登录时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'login_date' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user' +GO + +INSERT [sys_user] ([user_id], [dept_id], [user_name], [nick_name], [user_type], [email], [phonenumber], [sex], [avatar], [password], [status], [del_flag], [login_ip], [login_date], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1, 103, N'admin', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@163.com', N'15888888888', N'1', N'', N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'', getdate(), N'管理员') +GO +INSERT [sys_user] ([user_id], [dept_id], [user_name], [nick_name], [user_type], [email], [phonenumber], [sex], [avatar], [password], [status], [del_flag], [login_ip], [login_date], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (2, 105, N'ry', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@qq.com', N'15666666666', N'1', N'', N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'admin', getdate(), N'测试员') +GO + +CREATE TABLE [sys_user_post] +( + [user_id] bigint NOT NULL, + [post_id] bigint NOT NULL, + CONSTRAINT [PK__sys_user__CA534F799C04589B] PRIMARY KEY CLUSTERED ([user_id], [post_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post', + 'COLUMN', N'post_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户与岗位关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post' +GO + +INSERT [sys_user_post] ([user_id], [post_id]) VALUES (1, 1) +GO +INSERT [sys_user_post] ([user_id], [post_id]) VALUES (2, 2) +GO + +CREATE TABLE [sys_user_role] +( + [user_id] bigint NOT NULL, + [role_id] bigint NOT NULL, + CONSTRAINT [PK__sys_user__6EDEA153FB34D8F0] PRIMARY KEY CLUSTERED ([user_id], [role_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户和角色关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role' +GO + +INSERT [sys_user_role] ([user_id], [role_id]) VALUES (1, 1) +GO +INSERT [sys_user_role] ([user_id], [role_id]) VALUES (2, 2) +GO + +CREATE TABLE [sys_oss] +( + [oss_id] bigint NOT NULL, + [file_name] nvarchar(255) DEFAULT '' NOT NULL, + [original_name] nvarchar(255) DEFAULT '' NOT NULL, + [file_suffix] nvarchar(10) DEFAULT '' NOT NULL, + [url] nvarchar(500) NOT NULL, + [create_time] datetime2(7) NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [service] nvarchar(20) DEFAULT ('minio') NOT NULL, + CONSTRAINT [PK__sys_oss__91241EA442389F0D] PRIMARY KEY CLUSTERED ([oss_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'对象存储主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'oss_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'文件名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'file_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'原名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'original_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'文件后缀名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'file_suffix' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'URL地址', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'url' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'上传人', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'服务商', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'service' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'OSS对象存储表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss' +GO + +CREATE TABLE [sys_oss_config] +( + [oss_config_id] bigint NOT NULL, + [config_key] nvarchar(20) DEFAULT '' NOT NULL, + [access_key] nvarchar(255) DEFAULT '' NULL, + [secret_key] nvarchar(255) DEFAULT '' NULL, + [bucket_name] nvarchar(255) DEFAULT '' NULL, + [prefix] nvarchar(255) DEFAULT '' NULL, + [endpoint] nvarchar(255) DEFAULT '' NULL, + [domain] nvarchar(255) DEFAULT '' NULL, + [is_https] nchar(1) DEFAULT ('N') NULL, + [region] nvarchar(255) DEFAULT '' NULL, + [access_policy] nchar(1) DEFAULT ('1') NOT NULL, + [status] nchar(1) DEFAULT ('1') NULL, + [ext1] nvarchar(255) DEFAULT '' NULL, + [create_by] nvarchar(64) DEFAULT '' NULL, + [create_time] datetime2(7) NULL, + [update_by] nvarchar(64) DEFAULT '' NULL, + [update_time] datetime2(7) NULL, + [remark] nvarchar(500) NULL, + CONSTRAINT [PK__sys_oss___BFBDE87009ED2882] PRIMARY KEY CLUSTERED ([oss_config_id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主建', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'oss_config_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'配置key', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'config_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'accessKey', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'access_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'秘钥', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'secret_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'桶名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'bucket_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'前缀', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'prefix' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'访问站点', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'endpoint' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'自定义域名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'domain' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'是否https(Y=是,N=否)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'is_https' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'域', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'region' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'桶权限类型(0=private 1=public 2=custom)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'access_policy' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'状态(0=正常,1=停用)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'status' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'ext1' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'备注', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'remark' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'对象存储配置表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config' +GO + +INSERT INTO [sys_oss_config] ([oss_config_id], [config_key], [access_key], [secret_key], [bucket_name], [prefix], [endpoint], [domain], [is_https], [region], [access_policy], [status], [ext1], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (N'1', N'minio', N'rzdata', N'ruoyi123', N'rzdata', N'', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'0', N'', N'admin', getdate(), N'admin', getdate(), NULL) +GO +INSERT INTO [sys_oss_config] ([oss_config_id], [config_key], [access_key], [secret_key], [bucket_name], [prefix], [endpoint], [domain], [is_https], [region], [access_policy], [status], [ext1], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (N'2', N'qiniu', N'XXXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'rzdata', N'', N's3-cn-north-1.qiniucs.com', N'',N'N', N'', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) +GO +INSERT INTO [sys_oss_config] ([oss_config_id], [config_key], [access_key], [secret_key], [bucket_name], [prefix], [endpoint], [domain], [is_https], [region], [access_policy], [status], [ext1], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (N'3', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'rzdata', N'', N'oss-cn-beijing.aliyuncs.com', N'',N'N', N'', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) +GO +INSERT INTO [sys_oss_config] ([oss_config_id], [config_key], [access_key], [secret_key], [bucket_name], [prefix], [endpoint], [domain], [is_https], [region], [access_policy], [status], [ext1], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (N'4', N'qcloud', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'rzdata-1250000000', N'', N'cos.ap-beijing.myqcloud.com', N'',N'N', N'ap-beijing', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) +GO +INSERT INTO [sys_oss_config] ([oss_config_id], [config_key], [access_key], [secret_key], [bucket_name], [prefix], [endpoint], [domain], [is_https], [region], [access_policy], [status], [ext1], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (N'5', N'image', N'rzdata', N'ruoyi123', N'rzdata', N'image', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) +GO diff --git a/script/sql/sqlserver/sqlserver_test.sql b/script/sql/sqlserver/sqlserver_test.sql new file mode 100644 index 0000000..77fead3 --- /dev/null +++ b/script/sql/sqlserver/sqlserver_test.sql @@ -0,0 +1,478 @@ +CREATE TABLE [test_demo] +( + [id] bigint NOT NULL, + [dept_id] bigint NULL, + [user_id] bigint NULL, + [order_num] int DEFAULT ((0)) NULL, + [test_key] nvarchar(255) NULL, + [value] nvarchar(255) NULL, + [version] int DEFAULT ((0)) NULL, + [create_time] datetime2(0) NULL, + [create_by] nvarchar(64) NULL, + [update_time] datetime2(0) NULL, + [update_by] nvarchar(64) NULL, + [del_flag] int DEFAULT ((0)) NULL, + CONSTRAINT [PK__test_dem__3213E83F176051C8] PRIMARY KEY CLUSTERED ([id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'部门id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'dept_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'用户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'user_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'排序号', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'order_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'key键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'test_key' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'值', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'value' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'version' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'update_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'删除标志', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'测试单表', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo' +GO + +CREATE TABLE [test_tree] +( + [id] bigint NOT NULL, + [parent_id] bigint DEFAULT ((0)) NULL, + [dept_id] bigint NULL, + [user_id] bigint NULL, + [tree_name] nvarchar(255) NULL, + [version] int DEFAULT ((0)) NULL, + [create_time] datetime2(0) NULL, + [create_by] nvarchar(64) NULL, + [update_time] datetime2(0) NULL, + [update_by] nvarchar(64) NULL, + [del_flag] int DEFAULT ((0)) NULL, + CONSTRAINT [PK__test_tre__3213E83FC75A1B63] PRIMARY KEY CLUSTERED ([id]) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'父id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'parent_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'部门id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'dept_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'用户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'user_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'值', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'tree_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'version' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'update_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'删除标志', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'测试树表', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree' +GO + +INSERT [sys_user] ([user_id], [dept_id], [user_name], [nick_name], [user_type], [email], [phonenumber], [sex], [avatar], [password], [status], [del_flag], [login_ip], [login_date], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (3, 108, N'test', N'本部门及以下 密码666666', N'sys_user', N'', N'', N'0', N'', N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'test', getdate(), NULL); +GO +INSERT [sys_user] ([user_id], [dept_id], [user_name], [nick_name], [user_type], [email], [phonenumber], [sex], [avatar], [password], [status], [del_flag], [login_ip], [login_date], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (4, 102, N'test1', N'仅本人 密码666666', N'sys_user', N'', N'', N'0', N'', N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'test1', getdate(), NULL); +GO + +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (5, N'测试菜单', 0, 5, N'demo', NULL, 1, 0, N'M', N'0', N'0', NULL, N'star', N'admin', getdate(), NULL, NULL, N''); +GO + +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1500, N'测试单表', 5, 1, N'demo', N'demo/demo/index', 1, 0, N'C', N'0', N'0', N'demo:demo:list', N'#', N'admin', getdate(), N'', NULL, N'测试单表菜单'); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1501, N'测试单表查询', 1500, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:query', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1502, N'测试单表新增', 1500, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:add', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1503, N'测试单表修改', 1500, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:edit', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1504, N'测试单表删除', 1500, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:remove', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1505, N'测试单表导出', 1500, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:export', N'#', N'admin', getdate(), N'', NULL, N''); +GO + +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1506, N'测试树表', 5, 1, N'tree', N'demo/tree/index', 1, 0, N'C', N'0', N'0', N'demo:tree:list', N'#', N'admin', getdate(), N'', NULL, N'测试树表菜单'); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1507, N'测试树表查询', 1506, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:query', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1508, N'测试树表新增', 1506, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:add', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1509, N'测试树表修改', 1506, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:edit', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1510, N'测试树表删除', 1506, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:remove', N'#', N'admin', getdate(), N'', NULL, N''); +GO +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1511, N'测试树表导出', 1506, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:export', N'#', N'admin', getdate(), N'', NULL, N''); +GO + +INSERT [sys_role] ([role_id], [role_name], [role_key], [role_sort], [data_scope], [menu_check_strictly], [dept_check_strictly], [status], [del_flag], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (3, N'本部门及以下', N'test1', 3, N'4', 1, 1, N'0', N'0', N'admin', getdate(), N'admin', NULL, NULL); +GO +INSERT [sys_role] ([role_id], [role_name], [role_key], [role_sort], [data_scope], [menu_check_strictly], [dept_check_strictly], [status], [del_flag], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (4, N'仅本人', N'test2', 4, N'5', 1, 1, N'0', N'0', N'admin', getdate(), N'admin', NULL, NULL); +GO + +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 5); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 100); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 101); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 102); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 103); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 104); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 105); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 106); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 107); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 108); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 500); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 501); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1001); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1002); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1003); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1004); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1005); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1006); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1007); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1008); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1009); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1010); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1011); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1012); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1013); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1014); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1015); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1016); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1017); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1018); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1019); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1020); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1021); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1022); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1023); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1024); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1025); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1026); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1027); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1028); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1029); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1030); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1031); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1032); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1033); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1034); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1035); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1036); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1037); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1038); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1039); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1040); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1041); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1042); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1043); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1044); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1045); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1500); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1501); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1502); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1503); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1504); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1505); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1506); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1507); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1508); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1509); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1510); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (3, 1511); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 5); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1500); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1501); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1502); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1503); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1504); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1505); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1506); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1507); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1508); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1509); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1510); +GO +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (4, 1511); +GO + +INSERT [sys_user_role] ([user_id], [role_id]) VALUES (3, 3); +GO +INSERT [sys_user_role] ([user_id], [role_id]) VALUES (4, 4); +GO + +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (1, 102, 4, 1, N'测试数据权限', N'测试', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (2, 102, 3, 2, N'子节点1', N'111', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (3, 102, 3, 3, N'子节点2', N'222', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (4, 108, 4, 4, N'测试数据', N'demo', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (5, 108, 3, 13, N'子节点11', N'1111', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (6, 108, 3, 12, N'子节点22', N'2222', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (7, 108, 3, 11, N'子节点33', N'3333', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (8, 108, 3, 10, N'子节点44', N'4444', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (9, 108, 3, 9, N'子节点55', N'5555', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (10, 108, 3, 8, N'子节点66', N'6666', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (11, 108, 3, 7, N'子节点77', N'7777', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (12, 108, 3, 6, N'子节点88', N'8888', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_demo] ([id], [dept_id], [user_id], [order_num], [test_key], [value], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (13, 108, 3, 5, N'子节点99', N'9999', 0, getdate(), N'admin', NULL, NULL, 0); +GO + +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (1, 0, 102, 4, N'测试数据权限', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (2, 1, 102, 3, N'子节点1', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (3, 2, 102, 3, N'子节点2', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (4, 0, 108, 4, N'测试树1', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (5, 4, 108, 3, N'子节点11', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (6, 4, 108, 3, N'子节点22', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (7, 4, 108, 3, N'子节点33', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (8, 5, 108, 3, N'子节点44', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (9, 6, 108, 3, N'子节点55', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (10, 7, 108, 3, N'子节点66', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (11, 7, 108, 3, N'子节点77', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (12, 10, 108, 3, N'子节点88', 0, getdate(), N'admin', NULL, NULL, 0); +GO +INSERT [test_tree] ([id], [parent_id], [dept_id], [user_id], [tree_name], [version], [create_time], [create_by], [update_time], [update_by], [del_flag]) VALUES (13, 10, 108, 3, N'子节点99', 0, getdate(), N'admin', NULL, NULL, 0); +GO diff --git a/script/sql/tables_xxl_job.sql b/script/sql/tables_xxl_job.sql new file mode 100644 index 0000000..236c557 --- /dev/null +++ b/script/sql/tables_xxl_job.sql @@ -0,0 +1,119 @@ +# +# XXL-JOB v2.3.0 +# Copyright (c) 2015-present, xuxueli. + +SET NAMES utf8mb4; + +CREATE TABLE `xxl_job_info` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `job_group` int(11) NOT NULL COMMENT '执行器主键ID', + `job_desc` varchar(255) NOT NULL, + `add_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `author` varchar(64) DEFAULT NULL COMMENT '作者', + `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件', + `schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型', + `schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置,值含义取决于调度类型', + `misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING' COMMENT '调度过期策略', + `executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略', + `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', + `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', + `executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略', + `executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位秒', + `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', + `glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型', + `glue_source` mediumtext COMMENT 'GLUE源代码', + `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注', + `glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间', + `child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔', + `trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态:0-停止,1-运行', + `trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间', + `trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `job_group` int(11) NOT NULL COMMENT '执行器主键ID', + `job_id` int(11) NOT NULL COMMENT '任务,主键ID', + `executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址', + `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', + `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', + `executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数,格式如 1/2', + `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', + `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', + `trigger_code` int(11) NOT NULL COMMENT '调度-结果', + `trigger_msg` text COMMENT '调度-日志', + `handle_time` datetime DEFAULT NULL COMMENT '执行-时间', + `handle_code` int(11) NOT NULL COMMENT '执行-状态', + `handle_msg` text COMMENT '执行-日志', + `alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态:0-默认、1-无需告警、2-告警成功、3-告警失败', + PRIMARY KEY (`id`), + KEY `I_trigger_time` (`trigger_time`), + KEY `I_handle_code` (`handle_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_log_report` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `trigger_day` datetime DEFAULT NULL COMMENT '调度-时间', + `running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量', + `suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量', + `fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量', + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_logglue` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `job_id` int(11) NOT NULL COMMENT '任务,主键ID', + `glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型', + `glue_source` mediumtext COMMENT 'GLUE源代码', + `glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注', + `add_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_registry` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `registry_group` varchar(50) NOT NULL, + `registry_key` varchar(255) NOT NULL, + `registry_value` varchar(255) NOT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_group` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `app_name` varchar(64) NOT NULL COMMENT '执行器AppName', + `title` varchar(12) NOT NULL COMMENT '执行器名称', + `address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入', + `address_list` text COMMENT '执行器地址列表,多地址逗号分隔', + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(50) NOT NULL COMMENT '账号', + `password` varchar(50) NOT NULL COMMENT '密码', + `role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员', + `permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割', + PRIMARY KEY (`id`), + UNIQUE KEY `i_username` (`username`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_lock` ( + `lock_name` varchar(50) NOT NULL COMMENT '锁名称', + PRIMARY KEY (`lock_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`, `update_time`) VALUES (1, 'xxl-job-executor', '示例执行器', 0, NULL, '2018-11-03 22:21:31' ); +INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, `schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'CRON', '0 0 0 * * ? *', 'DO_NOTHING', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', ''); +INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL); +INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock'); + +commit; + diff --git a/script/sql/test.sql b/script/sql/test.sql new file mode 100644 index 0000000..6232bab --- /dev/null +++ b/script/sql/test.sql @@ -0,0 +1,171 @@ +DROP TABLE if EXISTS test_demo; +CREATE TABLE test_demo +( + id bigint(0) NOT NULL COMMENT '主键', + dept_id bigint(0) NULL DEFAULT NULL COMMENT '部门id', + user_id bigint(0) NULL DEFAULT NULL COMMENT '用户id', + order_num int(0) NULL DEFAULT 0 COMMENT '排序号', + test_key varchar(255) NULL DEFAULT NULL COMMENT 'key键', + value varchar(255) NULL DEFAULT NULL COMMENT '值', + version int(0) NULL DEFAULT 0 COMMENT '版本', + create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间', + create_by varchar(64) NULL DEFAULT NULL COMMENT '创建人', + update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间', + update_by varchar(64) NULL DEFAULT NULL COMMENT '更新人', + del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志', + PRIMARY KEY (id) USING BTREE +) ENGINE = InnoDB COMMENT = '测试单表'; + +DROP TABLE if EXISTS test_tree; +CREATE TABLE test_tree +( + id bigint(0) NOT NULL COMMENT '主键', + parent_id bigint(0) NULL DEFAULT 0 COMMENT '父id', + dept_id bigint(0) NULL DEFAULT NULL COMMENT '部门id', + user_id bigint(0) NULL DEFAULT NULL COMMENT '用户id', + tree_name varchar(255) NULL DEFAULT NULL COMMENT '值', + version int(0) NULL DEFAULT 0 COMMENT '版本', + create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间', + create_by varchar(64) NULL DEFAULT NULL COMMENT '创建人', + update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间', + update_by varchar(64) NULL DEFAULT NULL COMMENT '更新人', + del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志', + PRIMARY KEY (id) USING BTREE +) ENGINE = InnoDB COMMENT = '测试树表'; + +INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), 'test', sysdate(), NULL); +INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), 'test1', sysdate(), NULL); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 'admin', sysdate(), NULL, NULL, ''); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', sysdate(), '', NULL, '测试单表菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', sysdate(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', sysdate(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', sysdate(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', sysdate(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', sysdate(), '', NULL, ''); + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', sysdate(), '', NULL, '测试树表菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', sysdate(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', sysdate(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', sysdate(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', sysdate(), '', NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', sysdate(), '', NULL, ''); + +INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 'admin', sysdate(), 'admin', NULL, NULL); +INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (4, '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 'admin', sysdate(), 'admin', NULL, NULL); + +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511); + +INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3); +INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4); + +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 102, 4, 1, '测试数据权限', '测试', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 102, 3, 2, '子节点1', '111', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 102, 3, 3, '子节点2', '222', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 108, 4, 4, '测试数据', 'demo', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 108, 3, 13, '子节点11', '1111', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 108, 3, 12, '子节点22', '2222', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 108, 3, 11, '子节点33', '3333', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 108, 3, 10, '子节点44', '4444', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 108, 3, 9, '子节点55', '5555', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 108, 3, 8, '子节点66', '6666', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 108, 3, 7, '子节点77', '7777', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 108, 3, 6, '子节点88', '8888', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 108, 3, 5, '子节点99', '9999', 0, sysdate(), 'admin', NULL, NULL, 0); + +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 0, 102, 4, '测试数据权限', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 1, 102, 3, '子节点1', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 2, 102, 3, '子节点2', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 0, 108, 4, '测试树1', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 4, 108, 3, '子节点11', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 4, 108, 3, '子节点22', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 4, 108, 3, '子节点33', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 5, 108, 3, '子节点44', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 6, 108, 3, '子节点55', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 7, 108, 3, '子节点66', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 7, 108, 3, '子节点77', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 10, 108, 3, '子节点88', 0, sysdate(), 'admin', NULL, NULL, 0); +INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 10, 108, 3, '子节点99', 0, sysdate(), 'admin', NULL, NULL, 0); diff --git a/script/sql/update/oracle/update-4.1-4.2.sql b/script/sql/update/oracle/update-4.1-4.2.sql new file mode 100644 index 0000000..b315f53 --- /dev/null +++ b/script/sql/update/oracle/update-4.1-4.2.sql @@ -0,0 +1,12 @@ +ALTER TABLE "SYS_OSS_CONFIG" ADD ("DOMAIN" VARCHAR2(255)); + +COMMENT ON COLUMN "SYS_OSS_CONFIG"."DOMAIN" IS '自定义域名'; + +update sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1; +update sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2; +update sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3; +update sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4; + +insert into sys_oss_config values (5, 'image', 'rzdata', 'ruoyi123', 'rzdata', 'image', '127.0.0.1:9000', 'N', '', '1', '', NULL, 'admin', sysdate, 'admin', sysdate, ''); + +ALTER TABLE "GEN_TABLE_COLUMN" MODIFY ("TABLE_ID" NUMBER(20,0)); diff --git a/script/sql/update/oracle/update-4.2-4.3.sql b/script/sql/update/oracle/update-4.2-4.3.sql new file mode 100644 index 0000000..ec19d25 --- /dev/null +++ b/script/sql/update/oracle/update-4.2-4.3.sql @@ -0,0 +1,11 @@ +insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate, '', null, '缓存列表菜单'); + +delete from sys_menu WHERE menu_id = 116; + +update sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4; + +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate, '', null, ''); + +insert into sys_role_menu values ('2', '1050'); + +insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate, '', null, '其他操作'); diff --git a/script/sql/update/oracle/update-4.3-4.4.sql b/script/sql/update/oracle/update-4.3-4.4.sql new file mode 100644 index 0000000..77e12ed --- /dev/null +++ b/script/sql/update/oracle/update-4.3-4.4.sql @@ -0,0 +1,3 @@ +ALTER TABLE "SYS_OSS_CONFIG" ADD ("ACCESS_POLICY" CHAR(1) DEFAULT '1' NOT NULL); + +COMMENT ON COLUMN "SYS_OSS_CONFIG"."ACCESS_POLICY" IS '桶权限类型(0=private 1=public 2=custom)'; diff --git a/script/sql/update/postgres/update-4.1-4.2.sql b/script/sql/update/postgres/update-4.1-4.2.sql new file mode 100644 index 0000000..e85f37d --- /dev/null +++ b/script/sql/update/postgres/update-4.1-4.2.sql @@ -0,0 +1,11 @@ +ALTER TABLE "sys_oss_config" ADD COLUMN "domain" varchar(255); + +COMMENT ON COLUMN "sys_oss_config"."domain" IS '自定义域名'; + +update sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1; +update sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2; +update sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3; +update sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4; + +insert into sys_oss_config values (5, 'image', 'rzdata', 'ruoyi123', 'rzdata', 'image', '127.0.0.1:9000', 'N', '', '1', '', 'admin', now(), 'admin', now(), NULL, ''); + diff --git a/script/sql/update/postgres/update-4.2-4.3.sql b/script/sql/update/postgres/update-4.2-4.3.sql new file mode 100644 index 0000000..9479f3f --- /dev/null +++ b/script/sql/update/postgres/update-4.2-4.3.sql @@ -0,0 +1,19 @@ +insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', '1', '0', 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', now(), '', null, '缓存列表菜单'); + +delete from sys_menu WHERE menu_id = 116; + +update sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4; + +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', now(), '', null, ''); + +insert into sys_role_menu values ('2', '1050'); + +-- 字符串自动转时间 避免框架时间查询报错问题 +create or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$ +select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss'); +$$ language sql strict ; + +create cast (varchar as timestamptz) with function cast_varchar_to_timestamp as IMPLICIT; + +insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', now(), '', null, '其他操作'); + diff --git a/script/sql/update/postgres/update-4.3-4.4.sql b/script/sql/update/postgres/update-4.3-4.4.sql new file mode 100644 index 0000000..e74357a --- /dev/null +++ b/script/sql/update/postgres/update-4.3-4.4.sql @@ -0,0 +1,3 @@ +ALTER TABLE "sys_oss_config" ADD COLUMN "access_policy" char(1) NOT NULL DEFAULT '1'::bpchar; + +COMMENT ON COLUMN "sys_oss_config"."access_policy" IS '桶权限类型(0=private 1=public 2=custom)'; diff --git a/script/sql/update/sqlserver/update-4.1-4.2.sql b/script/sql/update/sqlserver/update-4.1-4.2.sql new file mode 100644 index 0000000..96d1014 --- /dev/null +++ b/script/sql/update/sqlserver/update-4.1-4.2.sql @@ -0,0 +1,27 @@ +ALTER TABLE [sys_oss_config] ADD [domain] nvarchar(255) DEFAULT '' NULL +GO + +EXEC sp_addextendedproperty +'MS_Description', N'自定义域名', +'SCHEMA', N'dbo', +'TABLE', N'sys_oss_config', +'COLUMN', N'domain' +GO + +UPDATE [sys_oss_config] SET [access_key] = N'rzdata', [secret_key] = N'ruoyi123', [endpoint] = N'127.0.0.1:9000' WHERE [oss_config_id] = 1 +GO + +UPDATE [sys_oss_config] SET [endpoint] = N's3-cn-north-1.qiniucs.com' WHERE [oss_config_id] = 2 +GO + +UPDATE [sys_oss_config] SET [endpoint] = N'oss-cn-beijing.aliyuncs.com' WHERE [oss_config_id] = 3 +GO + +UPDATE [sys_oss_config] SET [endpoint] = N'cos.ap-beijing.myqcloud.com' WHERE [oss_config_id] = 4 +GO + +INSERT INTO [sys_oss_config] ([oss_config_id], [config_key], [access_key], [secret_key], [bucket_name], [prefix], [endpoint], [domain], [is_https], [region], [status], [ext1], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (N'5', N'image', N'rzdata', N'ruoyi123', N'rzdata', N'image', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) +GO + +ALTER TABLE [gen_table_column] ALTER COLUMN [table_id] bigint NULL +GO diff --git a/script/sql/update/sqlserver/update-4.2-4.3.sql b/script/sql/update/sqlserver/update-4.2-4.3.sql new file mode 100644 index 0000000..19e5a9f --- /dev/null +++ b/script/sql/update/sqlserver/update-4.2-4.3.sql @@ -0,0 +1,17 @@ +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (112, N'缓存列表', 2, 6, N'cacheList', N'monitor/cache/list', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis-list', N'admin', getdate(), N'', NULL, N'缓存列表菜单') +GO + +delete from sys_menu WHERE menu_id = 116; +GO + +update sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4 +GO + +INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', N'admin', getdate(), N'', null, N'') +GO + +INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1050) +GO + +INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (29, 99, N'其他', N'0', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'其他操作'); +GO diff --git a/script/sql/update/sqlserver/update-4.3-4.4.sql b/script/sql/update/sqlserver/update-4.3-4.4.sql new file mode 100644 index 0000000..8e36db5 --- /dev/null +++ b/script/sql/update/sqlserver/update-4.3-4.4.sql @@ -0,0 +1,9 @@ +ALTER TABLE [sys_oss_config] ADD [access_policy] nchar(1) DEFAULT ('1') NOT NULL +GO + +EXEC sp_addextendedproperty +'MS_Description', N'桶权限类型(0=private 1=public 2=custom)', +'SCHEMA', N'dbo', +'TABLE', N'sys_oss_config', +'COLUMN', N'access_policy' +GO diff --git a/script/sql/update/update-3.X-4.0.sql b/script/sql/update/update-3.X-4.0.sql new file mode 100644 index 0000000..aac1e6f --- /dev/null +++ b/script/sql/update/update-3.X-4.0.sql @@ -0,0 +1,6 @@ +ALTER TABLE `sys_user` MODIFY COLUMN `user_type` varchar(10) DEFAULT 'sys_user' COMMENT '用户类型(sys_user系统用户)' AFTER `nick_name`; + +UPDATE `sys_user` SET `nick_name` = '疯狂的狮子Li', `user_type` = 'sys_user', `email` = 'crazyLionLi@163.com' WHERE `user_id` = 1; +UPDATE `sys_user` SET `user_name` = 'lionli', `nick_name` = '疯狂的狮子Li', `user_type` = 'sys_user', `email` = 'crazyLionLi@163.com' WHERE `user_id` = 2; +UPDATE `sys_user` SET `nick_name` = '本部门及以下 密码666666', `user_type` = 'sys_user', `password` = '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne' WHERE `user_id` = 3; +UPDATE `sys_user` SET `nick_name` = '仅本人 密码666666', `user_type` = 'sys_user', `password` = '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne' WHERE `user_id` = 4; diff --git a/script/sql/update/update-4.0-4.1.sql b/script/sql/update/update-4.0-4.1.sql new file mode 100644 index 0000000..28e545a --- /dev/null +++ b/script/sql/update/update-4.0-4.1.sql @@ -0,0 +1,3 @@ +alter table sys_menu change query query_param varchar(255) default null comment '路由参数'; + +alter table sys_dept modify column ancestors varchar(500) null default '' comment '祖级列表'; diff --git a/script/sql/update/update-4.1-4.2.sql b/script/sql/update/update-4.1-4.2.sql new file mode 100644 index 0000000..e701fee --- /dev/null +++ b/script/sql/update/update-4.1-4.2.sql @@ -0,0 +1,14 @@ +alter table sys_oss_config add column domain varchar(255) null default '' COMMENT '自定义域名'; + +update sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1; +update sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2; +update sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3; +update sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4; + +insert into sys_oss_config values (5, 'image', 'rzdata', 'ruoyi123', 'rzdata', 'image', '127.0.0.1:9000', 'N', '', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL, ''); + +alter table gen_table_column modify column table_id bigint(0) null default null COMMENT '归属表编号'; + +alter table sys_notice modify column notice_id bigint(0) not null COMMENT '公告ID'; + +alter table sys_config modify column config_id bigint(0) not null COMMENT '参数主键'; diff --git a/script/sql/update/update-4.2-4.3.sql b/script/sql/update/update-4.2-4.3.sql new file mode 100644 index 0000000..900657d --- /dev/null +++ b/script/sql/update/update-4.2-4.3.sql @@ -0,0 +1,11 @@ +insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate(), '', null, '缓存列表菜单'); + +delete from sys_menu WHERE menu_id = 116; + +update sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4; + +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_role_menu values ('2', '1050'); + +insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作'); diff --git a/script/sql/update/update-4.3-4.4.sql b/script/sql/update/update-4.3-4.4.sql new file mode 100644 index 0000000..5a59987 --- /dev/null +++ b/script/sql/update/update-4.3-4.4.sql @@ -0,0 +1 @@ +ALTER TABLE sys_oss_config ADD COLUMN access_policy char(1) NOT NULL DEFAULT 1 COMMENT '桶权限类型(0=private 1=public 2=custom)' AFTER region; diff --git a/service/Dockerfile b/service/Dockerfile new file mode 100644 index 0000000..a348b28 --- /dev/null +++ b/service/Dockerfile @@ -0,0 +1,23 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER Lion Li + +RUN mkdir -p /rzdata/server/logs \ + /rzdata/server/temp \ + /rzdata/skywalking/agent + +WORKDIR /ruoyi/server + +ENV SERVER_PORT=8080 + +EXPOSE ${SERVER_PORT} + +ADD ./target/ruoyi-admin.jar ./app.jar + +ENTRYPOINT ["java", \ + "-Djava.security.egd=file:/dev/./urandom", \ + "-Dserver.port=${SERVER_PORT}", \ + # 应用名称 如果想区分集群节点监控 改成不同的名称即可 +# "-Dskywalking.agent.service_name=rzdata-server", \ +# "-javaagent:/rzdata/skywalking/agent/skywalking-agent.jar", \ + "-jar", "app.jar"] diff --git a/service/pom.xml b/service/pom.xml new file mode 100644 index 0000000..4d8b9e7 --- /dev/null +++ b/service/pom.xml @@ -0,0 +1,291 @@ + + + + rzdata-archetype + com.rzdata + 4.5.0 + + 4.0.0 + jar + service + + + web服务入口 + + + + + + org.springframework.boot + spring-boot-starter-web + + + spring-boot-starter-tomcat + org.springframework.boot + + + + + + org.springframework.boot + spring-boot-starter-undertow + + + + + org.apache.velocity + velocity-engine-core + + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.springdoc + springdoc-openapi-webmvc-core + + + + org.springdoc + springdoc-openapi-javadoc + + + + + com.mysql + mysql-connector-j + + + + com.oracle.database.jdbc + ojdbc8 + + + + org.postgresql + postgresql + + + + com.microsoft.sqlserver + mssql-jdbc + + + + + cn.dev33 + sa-token-spring-boot-starter + + + + cn.dev33 + sa-token-jwt + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.alibaba + easyexcel + + + + + org.yaml + snakeyaml + + + + + javax.servlet + javax.servlet-api + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + + + + com.baomidou + mybatis-plus-boot-starter + + + + + p6spy + p6spy + + + + cn.hutool + hutool-core + + + + cn.hutool + hutool-http + + + + cn.hutool + hutool-captcha + + + + cn.hutool + hutool-jwt + + + + cn.hutool + hutool-extra + + + + com.sun.mail + jakarta.mail + + + + org.projectlombok + lombok + + + + de.codecentric + spring-boot-admin-starter-client + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-configuration-processor + + + + + org.redisson + redisson-spring-boot-starter + + + + org.redisson + redisson-spring-data-27 + + + + com.baomidou + lock4j-redisson-spring-boot-starter + + + + + com.xuxueli + xxl-job-core + + + + com.alibaba + transmittable-thread-local + + + + com.amazonaws + aws-java-sdk-s3 + + + + + com.aliyun + dysmsapi20170525 + + + + com.tencentcloudapi + tencentcloud-sdk-java-sms + + + com.squareup.okio + okio + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + + + + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.2.2 + + false + ${project.artifactId} + + + + + + diff --git a/service/src/main/java/com/rzdata/RuoYiApplication.java b/service/src/main/java/com/rzdata/RuoYiApplication.java new file mode 100644 index 0000000..ce4a8ab --- /dev/null +++ b/service/src/main/java/com/rzdata/RuoYiApplication.java @@ -0,0 +1,24 @@ +package com.rzdata; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; + +/** + * 启动程序 + * + * @author rzdata + */ + +@SpringBootApplication +public class RuoYiApplication { + + public static void main(String[] args) { + System.setProperty("spring.devtools.restart.enabled", "false"); + SpringApplication application = new SpringApplication(RuoYiApplication.class); + application.setApplicationStartup(new BufferingApplicationStartup(2048)); + application.run(args); + System.out.println("(♥◠‿◠)ノ゙ RuoYi-Vue-Plus启动成功 ლ(´ڡ`ლ)゙"); + } + +} diff --git a/service/src/main/java/com/rzdata/RuoYiServletInitializer.java b/service/src/main/java/com/rzdata/RuoYiServletInitializer.java new file mode 100644 index 0000000..360d3f5 --- /dev/null +++ b/service/src/main/java/com/rzdata/RuoYiServletInitializer.java @@ -0,0 +1,18 @@ +package com.rzdata; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author rzdata + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(RuoYiApplication.class); + } + +} diff --git a/service/src/main/java/com/rzdata/common/annotation/CellMerge.java b/service/src/main/java/com/rzdata/common/annotation/CellMerge.java new file mode 100644 index 0000000..856f39d --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/CellMerge.java @@ -0,0 +1,24 @@ +package com.rzdata.common.annotation; + +import com.rzdata.common.excel.CellMergeStrategy; + +import java.lang.annotation.*; + +/** + * excel 列单元格合并(合并列相同项) + * + * 需搭配 {@link CellMergeStrategy} 策略使用 + * + * @author Lion Li + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface CellMerge { + + /** + * col index + */ + int index() default -1; + +} diff --git a/service/src/main/java/com/rzdata/common/annotation/DataColumn.java b/service/src/main/java/com/rzdata/common/annotation/DataColumn.java new file mode 100644 index 0000000..da8c1cf --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/DataColumn.java @@ -0,0 +1,28 @@ +package com.rzdata.common.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限 + * + * 一个注解只能对应一个模板 + * + * @author Lion Li + * @version 3.5.0 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataColumn { + + /** + * 占位符关键字 + */ + String[] key() default "deptName"; + + /** + * 占位符替换值 + */ + String[] value() default "dept_id"; + +} diff --git a/service/src/main/java/com/rzdata/common/annotation/DataPermission.java b/service/src/main/java/com/rzdata/common/annotation/DataPermission.java new file mode 100644 index 0000000..7c61821 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/DataPermission.java @@ -0,0 +1,18 @@ +package com.rzdata.common.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限组 + * + * @author Lion Li + * @version 3.5.0 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataPermission { + + DataColumn[] value(); + +} diff --git a/service/src/main/java/com/rzdata/common/annotation/DictDataMapper.java b/service/src/main/java/com/rzdata/common/annotation/DictDataMapper.java new file mode 100644 index 0000000..1c46579 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/DictDataMapper.java @@ -0,0 +1,27 @@ +package com.rzdata.common.annotation; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.rzdata.common.jackson.DictDataJsonSerializer; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 字典数据映射注解 + * + * @author itino + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +@JacksonAnnotationsInside +@JsonSerialize(using = DictDataJsonSerializer.class) +public @interface DictDataMapper { + + /** + * 设置字典的type值 (如: sys_user_sex) + */ + String dictType() default ""; +} diff --git a/service/src/main/java/com/rzdata/common/annotation/ExcelDictFormat.java b/service/src/main/java/com/rzdata/common/annotation/ExcelDictFormat.java new file mode 100644 index 0000000..ffadc7c --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/ExcelDictFormat.java @@ -0,0 +1,30 @@ +package com.rzdata.common.annotation; + +import java.lang.annotation.*; + +/** + * 字典格式化 + * + * @author Lion Li + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelDictFormat { + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + String separator() default ","; + +} diff --git a/service/src/main/java/com/rzdata/common/annotation/Log.java b/service/src/main/java/com/rzdata/common/annotation/Log.java new file mode 100644 index 0000000..48d43a4 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/Log.java @@ -0,0 +1,41 @@ +package com.rzdata.common.annotation; + +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.enums.OperatorType; + +import java.lang.annotation.*; + +/** + * 自定义操作日志记录注解 + * + * @author rzdata + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log { + /** + * 模块 + */ + String title() default ""; + + /** + * 功能 + */ + BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + boolean isSaveResponseData() default true; +} diff --git a/service/src/main/java/com/rzdata/common/annotation/RateLimiter.java b/service/src/main/java/com/rzdata/common/annotation/RateLimiter.java new file mode 100644 index 0000000..2444153 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/RateLimiter.java @@ -0,0 +1,36 @@ +package com.rzdata.common.annotation; + +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.enums.LimitType; + +import java.lang.annotation.*; + +/** + * 限流注解 + * + * @author Lion Li + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter { + /** + * 限流key + */ + String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + int time() default 60; + + /** + * 限流次数 + */ + int count() default 100; + + /** + * 限流类型 + */ + LimitType limitType() default LimitType.DEFAULT; +} diff --git a/service/src/main/java/com/rzdata/common/annotation/RepeatSubmit.java b/service/src/main/java/com/rzdata/common/annotation/RepeatSubmit.java new file mode 100644 index 0000000..252eaaa --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/RepeatSubmit.java @@ -0,0 +1,29 @@ +package com.rzdata.common.annotation; + +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; + +/** + * 自定义注解防止表单重复提交 + * + * @author Lion Li + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit { + + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + int interval() default 5000; + + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; + + /** + * 提示消息 支持国际化 格式为 {code} + */ + String message() default "{repeat.submit.message}"; + +} diff --git a/service/src/main/java/com/rzdata/common/annotation/Sensitive.java b/service/src/main/java/com/rzdata/common/annotation/Sensitive.java new file mode 100644 index 0000000..92776d9 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/annotation/Sensitive.java @@ -0,0 +1,24 @@ +package com.rzdata.common.annotation; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.rzdata.common.enums.SensitiveStrategy; +import com.rzdata.common.jackson.SensitiveJsonSerializer; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据脱敏注解 + * + * @author zhujie + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@JacksonAnnotationsInside +@JsonSerialize(using = SensitiveJsonSerializer.class) +public @interface Sensitive { + SensitiveStrategy strategy(); +} diff --git a/service/src/main/java/com/rzdata/common/captcha/UnsignedMathGenerator.java b/service/src/main/java/com/rzdata/common/captcha/UnsignedMathGenerator.java new file mode 100644 index 0000000..0efdb37 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/captcha/UnsignedMathGenerator.java @@ -0,0 +1,85 @@ +package com.rzdata.common.captcha; + +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.core.math.Calculator; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.RandomUtil; +import com.rzdata.common.utils.StringUtils; + +/** + * 无符号计算生成器 + * + * @author Lion Li + */ +public class UnsignedMathGenerator implements CodeGenerator { + + private static final long serialVersionUID = -5514819971774091076L; + + private static final String OPERATORS = "+-*"; + + /** + * 参与计算数字最大长度 + */ + private final int numberLength; + + /** + * 构造 + */ + public UnsignedMathGenerator() { + this(2); + } + + /** + * 构造 + * + * @param numberLength 参与计算最大数字位数 + */ + public UnsignedMathGenerator(int numberLength) { + this.numberLength = numberLength; + } + + @Override + public String generate() { + final int limit = getLimit(); + int a = RandomUtil.randomInt(limit); + int b = RandomUtil.randomInt(limit); + String max = Integer.toString(Math.max(a,b)); + String min = Integer.toString(Math.min(a,b)); + max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE); + min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE); + + return max + RandomUtil.randomChar(OPERATORS) + min + '='; + } + + @Override + public boolean verify(String code, String userInputCode) { + int result; + try { + result = Integer.parseInt(userInputCode); + } catch (NumberFormatException e) { + // 用户输入非数字 + return false; + } + + final int calculateResult = (int) Calculator.conversion(code); + return result == calculateResult; + } + + /** + * 获取验证码长度 + * + * @return 验证码长度 + */ + public int getLength() { + return this.numberLength * 2 + 2; + } + + /** + * 根据长度获取参与计算数字最大值 + * + * @return 最大值 + */ + private int getLimit() { + return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength)); + } +} diff --git a/service/src/main/java/com/rzdata/common/config/RuoYiConfig.java b/service/src/main/java/com/rzdata/common/config/RuoYiConfig.java new file mode 100644 index 0000000..f6c2acb --- /dev/null +++ b/service/src/main/java/com/rzdata/common/config/RuoYiConfig.java @@ -0,0 +1,54 @@ +package com.rzdata.common.config; + +import lombok.Data; +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author Lion Li + */ + +@Data +@Component +@ConfigurationProperties(prefix = "rzdata") +public class RuoYiConfig { + + /** + * 项目名称 + */ + private String name; + + /** + * 版本 + */ + private String version; + + /** + * 版权年份 + */ + private String copyrightYear; + + /** + * 实例演示开关 + */ + private boolean demoEnabled; + + /** + * 缓存懒加载 + */ + private boolean cacheLazy; + + /** + * 获取地址开关 + */ + @Getter + private static boolean addressEnabled; + + public void setAddressEnabled(boolean addressEnabled) { + RuoYiConfig.addressEnabled = addressEnabled; + } + +} diff --git a/service/src/main/java/com/rzdata/common/constant/CacheConstants.java b/service/src/main/java/com/rzdata/common/constant/CacheConstants.java new file mode 100644 index 0000000..a8a08b9 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/constant/CacheConstants.java @@ -0,0 +1,49 @@ +package com.rzdata.common.constant; + +/** + * 缓存的key 常量 + * + * @author rzdata + */ +public interface CacheConstants { + + /** + * 登录用户 redis key + */ + String LOGIN_TOKEN_KEY = "Authorization:login:token:"; + + /** + * 在线用户 redis key + */ + String ONLINE_TOKEN_KEY = "online_tokens:"; + + /** + * 验证码 redis key + */ + String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + String SYS_DICT_KEY = "sys_dict:"; + + /** + * 防重提交 redis key + */ + String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; +} diff --git a/service/src/main/java/com/rzdata/common/constant/CacheNames.java b/service/src/main/java/com/rzdata/common/constant/CacheNames.java new file mode 100644 index 0000000..38d824f --- /dev/null +++ b/service/src/main/java/com/rzdata/common/constant/CacheNames.java @@ -0,0 +1,48 @@ +package com.rzdata.common.constant; + +/** + * 缓存组名称常量 + *

+ * key 格式为 cacheNames#ttl#maxIdleTime#maxSize + *

+ * ttl 过期时间 如果设置为0则不过期 默认为0 + * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 + * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 + *

+ * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500 + * + * @author Lion Li + */ +public interface CacheNames { + + /** + * 演示案例 + */ + String DEMO_CACHE = "demo:cache#60s#10m#20"; + + /** + * 系统配置 + */ + String SYS_CONFIG = "sys_config"; + + /** + * 数据字典 + */ + String SYS_DICT = "sys_dict"; + + /** + * OSS内容 + */ + String SYS_OSS = "sys_oss#30d"; + + /** + * OSS配置 + */ + String SYS_OSS_CONFIG = "sys_oss_config"; + + /** + * 在线用户 + */ + String ONLINE_TOKEN = "online_tokens"; + +} diff --git a/service/src/main/java/com/rzdata/common/constant/Constants.java b/service/src/main/java/com/rzdata/common/constant/Constants.java new file mode 100644 index 0000000..7d8eb09 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/constant/Constants.java @@ -0,0 +1,76 @@ +package com.rzdata.common.constant; + +/** + * 通用常量信息 + * + * @author rzdata + */ +public interface Constants { + + /** + * UTF-8 字符集 + */ + String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + String GBK = "GBK"; + + /** + * www主域 + */ + String WWW = "www."; + + /** + * http请求 + */ + String HTTP = "http://"; + + /** + * https请求 + */ + String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + String FAIL = "1"; + + /** + * 登录成功 + */ + String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + String LOGOUT = "Logout"; + + /** + * 注册 + */ + String REGISTER = "Register"; + + /** + * 登录失败 + */ + String LOGIN_FAIL = "Error"; + + /** + * 验证码有效期(分钟) + */ + Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + String TOKEN = "token"; + +} + diff --git a/service/src/main/java/com/rzdata/common/constant/GenConstants.java b/service/src/main/java/com/rzdata/common/constant/GenConstants.java new file mode 100644 index 0000000..7ac3b41 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/constant/GenConstants.java @@ -0,0 +1,193 @@ +package com.rzdata.common.constant; + +/** + * 代码生成通用常量 + * + * @author rzdata + */ +public interface GenConstants { + /** + * 单表(增删改查) + */ + String TPL_CRUD = "crud"; + + /** + * 树表(增删改查) + */ + String TPL_TREE = "tree"; + + /** + * 主子表(增删改查) + */ + String TPL_SUB = "sub"; + + /** + * 树编码字段 + */ + String TREE_CODE = "treeCode"; + + /** + * 树父编码字段 + */ + String TREE_PARENT_CODE = "treeParentCode"; + + /** + * 树名称字段 + */ + String TREE_NAME = "treeName"; + + /** + * 上级菜单ID字段 + */ + String PARENT_MENU_ID = "parentMenuId"; + + /** + * 上级菜单名称字段 + */ + String PARENT_MENU_NAME = "parentMenuName"; + + /** + * 数据库字符串类型 + */ + String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"}; + + /** + * 数据库文本类型 + */ + String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"}; + + /** + * 数据库时间类型 + */ + String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"}; + + /** + * 数据库数字类型 + */ + String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal"}; + + /** + * BO对象 不需要添加字段 + */ + String[] COLUMNNAME_NOT_ADD = {"create_by", "create_time", "del_flag", "update_by", + "update_time", "version"}; + + /** + * BO对象 不需要编辑字段 + */ + String[] COLUMNNAME_NOT_EDIT = {"create_by", "create_time", "del_flag", "update_by", + "update_time", "version"}; + + /** + * VO对象 不需要返回字段 + */ + String[] COLUMNNAME_NOT_LIST = {"create_by", "create_time", "del_flag", "update_by", + "update_time", "version"}; + + /** + * BO对象 不需要查询字段 + */ + String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark", "version"}; + + /** + * Entity基类字段 + */ + String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime"}; + + /** + * Tree基类字段 + */ + String[] TREE_ENTITY = {"parentName", "parentId", "children"}; + + /** + * 文本框 + */ + String HTML_INPUT = "input"; + + /** + * 文本域 + */ + String HTML_TEXTAREA = "textarea"; + + /** + * 下拉框 + */ + String HTML_SELECT = "select"; + + /** + * 单选框 + */ + String HTML_RADIO = "radio"; + + /** + * 复选框 + */ + String HTML_CHECKBOX = "checkbox"; + + /** + * 日期控件 + */ + String HTML_DATETIME = "datetime"; + + /** + * 图片上传控件 + */ + String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** + * 文件上传控件 + */ + String HTML_FILE_UPLOAD = "fileUpload"; + + /** + * 富文本控件 + */ + String HTML_EDITOR = "editor"; + + /** + * 字符串类型 + */ + String TYPE_STRING = "String"; + + /** + * 整型 + */ + String TYPE_INTEGER = "Integer"; + + /** + * 长整型 + */ + String TYPE_LONG = "Long"; + + /** + * 浮点型 + */ + String TYPE_DOUBLE = "Double"; + + /** + * 高精度计算类型 + */ + String TYPE_BIGDECIMAL = "BigDecimal"; + + /** + * 时间类型 + */ + String TYPE_DATE = "Date"; + + /** + * 模糊查询 + */ + String QUERY_LIKE = "LIKE"; + + /** + * 相等查询 + */ + String QUERY_EQ = "EQ"; + + /** + * 需要 + */ + String REQUIRE = "1"; +} diff --git a/service/src/main/java/com/rzdata/common/constant/HttpStatus.java b/service/src/main/java/com/rzdata/common/constant/HttpStatus.java new file mode 100644 index 0000000..bcf992f --- /dev/null +++ b/service/src/main/java/com/rzdata/common/constant/HttpStatus.java @@ -0,0 +1,93 @@ +package com.rzdata.common.constant; + +/** + * 返回状态码 + * + * @author Lion Li + */ +public interface HttpStatus { + /** + * 操作成功 + */ + int SUCCESS = 200; + + /** + * 对象创建成功 + */ + int CREATED = 201; + + /** + * 请求已经被接受 + */ + int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + int MOVED_PERM = 301; + + /** + * 重定向 + */ + int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + int BAD_REQUEST = 400; + + /** + * 未授权 + */ + int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + int ERROR = 500; + + /** + * 接口未实现 + */ + int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + int WARN = 601; +} diff --git a/service/src/main/java/com/rzdata/common/constant/UserConstants.java b/service/src/main/java/com/rzdata/common/constant/UserConstants.java new file mode 100644 index 0000000..6328858 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/constant/UserConstants.java @@ -0,0 +1,138 @@ +package com.rzdata.common.constant; + +/** + * 用户常量信息 + * + * @author rzdata + */ +public interface UserConstants { + + /** + * 平台内系统用户的唯一标志 + */ + String SYS_USER = "SYS_USER"; + + /** + * 正常状态 + */ + String NORMAL = "0"; + + /** + * 异常状态 + */ + String EXCEPTION = "1"; + + /** + * 用户正常状态 + */ + String USER_NORMAL = "0"; + + /** + * 用户封禁状态 + */ + String USER_DISABLE = "1"; + + /** + * 角色正常状态 + */ + String ROLE_NORMAL = "0"; + + /** + * 角色封禁状态 + */ + String ROLE_DISABLE = "1"; + + /** + * 部门正常状态 + */ + String DEPT_NORMAL = "0"; + + /** + * 部门停用状态 + */ + String DEPT_DISABLE = "1"; + + /** + * 字典正常状态 + */ + String DICT_NORMAL = "0"; + + /** + * 是否为系统默认(是) + */ + String YES = "Y"; + + /** + * 是否菜单外链(是) + */ + String YES_FRAME = "0"; + + /** + * 是否菜单外链(否) + */ + String NO_FRAME = "1"; + + /** + * 菜单正常状态 + */ + String MENU_NORMAL = "0"; + + /** + * 菜单停用状态 + */ + String MENU_DISABLE = "1"; + + /** + * 菜单类型(目录) + */ + String TYPE_DIR = "M"; + + /** + * 菜单类型(菜单) + */ + String TYPE_MENU = "C"; + + /** + * 菜单类型(按钮) + */ + String TYPE_BUTTON = "F"; + + /** + * Layout组件标识 + */ + String LAYOUT = "Layout"; + + /** + * ParentView组件标识 + */ + String PARENT_VIEW = "ParentView"; + + /** + * InnerLink组件标识 + */ + String INNER_LINK = "InnerLink"; + + /** + * 校验返回结果码 + */ + String UNIQUE = "0"; + String NOT_UNIQUE = "1"; + + /** + * 用户名长度限制 + */ + int USERNAME_MIN_LENGTH = 2; + int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + int PASSWORD_MIN_LENGTH = 5; + int PASSWORD_MAX_LENGTH = 20; + + /** + * 管理员ID + */ + Long ADMIN_ID = 1L; + +} diff --git a/service/src/main/java/com/rzdata/common/convert/ExcelBigNumberConvert.java b/service/src/main/java/com/rzdata/common/convert/ExcelBigNumberConvert.java new file mode 100644 index 0000000..aceefba --- /dev/null +++ b/service/src/main/java/com/rzdata/common/convert/ExcelBigNumberConvert.java @@ -0,0 +1,52 @@ +package com.rzdata.common.convert; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; + +/** + * 大数值转换 + * Excel 数值长度位15位 大于15位的数值转换位字符串 + * + * @author Lion Li + */ +@Slf4j +public class ExcelBigNumberConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return Convert.toLong(cellData.getData()); + } + + @Override + public WriteCellData convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNotNull(object)) { + String str = Convert.toStr(object); + if (str.length() > 15) { + return new WriteCellData<>(str); + } + } + WriteCellData cellData = new WriteCellData<>(new BigDecimal(object)); + cellData.setType(CellDataTypeEnum.NUMBER); + return cellData; + } + +} diff --git a/service/src/main/java/com/rzdata/common/convert/ExcelDictConvert.java b/service/src/main/java/com/rzdata/common/convert/ExcelDictConvert.java new file mode 100644 index 0000000..4d957a4 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/convert/ExcelDictConvert.java @@ -0,0 +1,73 @@ +package com.rzdata.common.convert; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.core.service.DictService; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; + +/** + * 字典格式化转换处理 + * + * @author Lion Li + */ +@Slf4j +public class ExcelDictConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Object.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + ExcelDictFormat anno = getAnnotation(contentProperty.getField()); + String type = anno.dictType(); + String label = cellData.getStringValue(); + String value; + if (StringUtils.isBlank(type)) { + value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator()); + } else { + value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator()); + } + return Convert.convert(contentProperty.getField().getType(), value); + } + + @Override + public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNull(object)) { + return new WriteCellData<>(""); + } + ExcelDictFormat anno = getAnnotation(contentProperty.getField()); + String type = anno.dictType(); + String value = Convert.toStr(object); + String label; + if (StringUtils.isBlank(type)) { + label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator()); + } else { + label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator()); + } + return new WriteCellData<>(label); + } + + private ExcelDictFormat getAnnotation(Field field) { + return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class); + } +} diff --git a/service/src/main/java/com/rzdata/common/core/controller/BaseController.java b/service/src/main/java/com/rzdata/common/core/controller/BaseController.java new file mode 100644 index 0000000..3a64d82 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/controller/BaseController.java @@ -0,0 +1,69 @@ +package com.rzdata.common.core.controller; + +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.StringUtils; + +/** + * web层通用数据处理 + * + * @author Lion Li + */ +public class BaseController { + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected R toAjax(int rows) { + return rows > 0 ? R.ok() : R.fail(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected R toAjax(boolean result) { + return result ? R.ok() : R.fail(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUser getLoginUser() { + return LoginHelper.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() { + return LoginHelper.getUserId(); + } + + /** + * 获取登录部门id + */ + public Long getDeptId() { + return LoginHelper.getDeptId(); + } + + /** + * 获取登录用户名 + */ + public String getUsername() { + return LoginHelper.getUsername(); + } +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/BaseEntity.java b/service/src/main/java/com/rzdata/common/core/domain/BaseEntity.java new file mode 100644 index 0000000..8f17d3f --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/BaseEntity.java @@ -0,0 +1,63 @@ +package com.rzdata.common.core.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author Lion Li + */ + +@Data +public class BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 搜索值 + */ + @JsonIgnore + @TableField(exist = false) + private String searchValue; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 请求参数 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/PageQuery.java b/service/src/main/java/com/rzdata/common/core/domain/PageQuery.java new file mode 100644 index 0000000..c46ef90 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/PageQuery.java @@ -0,0 +1,112 @@ +package com.rzdata.common.core.domain; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.sql.SqlUtil; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 分页查询实体类 + * + * @author Lion Li + */ + +@Data +public class PageQuery implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 分页大小 + */ + private Integer pageSize; + + /** + * 当前页数 + */ + private Integer pageNum; + + /** + * 排序列 + */ + private String orderByColumn; + + /** + * 排序的方向desc或者asc + */ + private String isAsc; + + /** + * 当前记录起始索引 默认值 + */ + public static final int DEFAULT_PAGE_NUM = 1; + + /** + * 每页显示记录数 默认值 默认查全部 + */ + public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + + public Page build() { + Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); + Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); + if (pageNum <= 0) { + pageNum = DEFAULT_PAGE_NUM; + } + Page page = new Page<>(pageNum, pageSize); + List orderItems = buildOrderItem(); + if (CollUtil.isNotEmpty(orderItems)) { + page.addOrder(orderItems); + } + return page; + } + + /** + * 构建排序 + * + * 支持的用法如下: + * {isAsc:"asc",orderByColumn:"id"} order by id asc + * {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc + * {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc + * {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc + */ + private List buildOrderItem() { + if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) { + return null; + } + String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); + orderBy = StringUtils.toUnderScoreCase(orderBy); + + // 兼容前端排序类型 + isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"}); + + String[] orderByArr = orderBy.split(","); + String[] isAscArr = isAsc.split(","); + if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) { + throw new ServiceException("排序参数有误"); + } + + List list = new ArrayList<>(); + // 每个字段各自排序 + for (int i = 0; i < orderByArr.length; i++) { + String orderByStr = orderByArr[i]; + String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i]; + if ("asc".equals(isAscStr)) { + list.add(OrderItem.asc(orderByStr)); + } else if ("desc".equals(isAscStr)) { + list.add(OrderItem.desc(orderByStr)); + } else { + throw new ServiceException("排序参数有误"); + } + } + return list; + } + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/R.java b/service/src/main/java/com/rzdata/common/core/domain/R.java new file mode 100644 index 0000000..0058aa7 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/R.java @@ -0,0 +1,107 @@ +package com.rzdata.common.core.domain; + +import com.rzdata.common.constant.HttpStatus; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class R implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 成功 + */ + public static final int SUCCESS = 200; + + /** + * 失败 + */ + public static final int FAIL = 500; + + private int code; + + private String msg; + + private T data; + + public static R ok() { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(String msg) { + return restResult(null, SUCCESS, msg); + } + + public static R ok(String msg, T data) { + return restResult(data, SUCCESS, msg); + } + + public static R fail() { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(String msg, T data) { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) { + return restResult(null, code, msg); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static R warn(String msg) { + return restResult(null, HttpStatus.WARN, msg); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static R warn(String msg, T data) { + return restResult(data, HttpStatus.WARN, msg); + } + + private static R restResult(T data, int code, String msg) { + R r = new R<>(); + r.setCode(code); + r.setData(data); + r.setMsg(msg); + return r; + } + + public static Boolean isError(R ret) { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/TreeEntity.java b/service/src/main/java/com/rzdata/common/core/domain/TreeEntity.java new file mode 100644 index 0000000..2a5d872 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/TreeEntity.java @@ -0,0 +1,39 @@ +package com.rzdata.common.core.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class TreeEntity extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 父菜单名称 + */ + @TableField(exist = false) + private String parentName; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 子部门 + */ + @TableField(exist = false) + private List children = new ArrayList<>(); + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/dto/RoleDTO.java b/service/src/main/java/com/rzdata/common/core/domain/dto/RoleDTO.java new file mode 100644 index 0000000..17923cd --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/dto/RoleDTO.java @@ -0,0 +1,38 @@ +package com.rzdata.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 角色 + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class RoleDTO implements Serializable { + + /** + * 角色ID + */ + private Long roleId; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 角色权限 + */ + private String roleKey; + + /** + * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) + */ + private String dataScope; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/dto/UserOnlineDTO.java b/service/src/main/java/com/rzdata/common/core/domain/dto/UserOnlineDTO.java new file mode 100644 index 0000000..3213d5d --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/dto/UserOnlineDTO.java @@ -0,0 +1,60 @@ +package com.rzdata.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 当前在线会话 + * + * @author rzdata + */ + +@Data +@NoArgsConstructor +public class UserOnlineDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 会话编号 + */ + private String tokenId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户名称 + */ + private String userName; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/entity/SysDept.java b/service/src/main/java/com/rzdata/common/core/domain/entity/SysDept.java new file mode 100644 index 0000000..df9a1ea --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/entity/SysDept.java @@ -0,0 +1,80 @@ +package com.rzdata.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.core.domain.TreeEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * 部门表 sys_dept + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dept") +public class SysDept extends TreeEntity { + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + @TableId(value = "dept_id") + private Long deptId; + + /** + * 部门名称 + */ + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + private String deptName; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空") + private Integer orderNum; + + /** + * 负责人 + */ + private String leader; + + /** + * 联系电话 + */ + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + private String phone; + + /** + * 邮箱 + */ + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + private String email; + + /** + * 部门状态:0正常,1停用 + */ + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 祖级列表 + */ + private String ancestors; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/entity/SysDictData.java b/service/src/main/java/com/rzdata/common/core/domain/entity/SysDictData.java new file mode 100644 index 0000000..40e8dd1 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/entity/SysDictData.java @@ -0,0 +1,100 @@ +package com.rzdata.common.core.domain.entity; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.convert.ExcelDictConvert; +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +/** + * 字典数据表 sys_dict_data + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dict_data") +@ExcelIgnoreUnannotated +public class SysDictData extends BaseEntity { + + /** + * 字典编码 + */ + @ExcelProperty(value = "字典编码") + @TableId(value = "dict_code") + private Long dictCode; + + /** + * 字典排序 + */ + @ExcelProperty(value = "字典排序") + private Integer dictSort; + + /** + * 字典标签 + */ + @ExcelProperty(value = "字典标签") + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + private String dictLabel; + + /** + * 字典键值 + */ + @ExcelProperty(value = "字典键值") + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + private String dictValue; + + /** + * 字典类型 + */ + @ExcelProperty(value = "字典类型") + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + private String dictType; + + /** + * 样式属性(其他样式扩展) + */ + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + private String cssClass; + + /** + * 表格字典样式 + */ + private String listClass; + + /** + * 是否默认(Y是 N否) + */ + @ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private String isDefault; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + private String remark; + + public boolean getDefault() { + return UserConstants.YES.equals(this.isDefault); + } + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/entity/SysDictType.java b/service/src/main/java/com/rzdata/common/core/domain/entity/SysDictType.java new file mode 100644 index 0000000..c6656c2 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/entity/SysDictType.java @@ -0,0 +1,65 @@ +package com.rzdata.common.core.domain.entity; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.convert.ExcelDictConvert; +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * 字典类型表 sys_dict_type + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dict_type") +@ExcelIgnoreUnannotated +public class SysDictType extends BaseEntity { + + /** + * 字典主键 + */ + @ExcelProperty(value = "字典主键") + @TableId(value = "dict_id") + private Long dictId; + + /** + * 字典名称 + */ + @ExcelProperty(value = "字典名称") + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + private String dictName; + + /** + * 字典类型 + */ + @ExcelProperty(value = "字典类型") + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + private String dictType; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/entity/SysMenu.java b/service/src/main/java/com/rzdata/common/core/domain/entity/SysMenu.java new file mode 100644 index 0000000..8565f05 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/entity/SysMenu.java @@ -0,0 +1,104 @@ +package com.rzdata.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.rzdata.common.core.domain.TreeEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * 菜单权限表 sys_menu + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_menu") +public class SysMenu extends TreeEntity { + + /** + * 菜单ID + */ + @TableId(value = "menu_id") + private Long menuId; + + /** + * 菜单名称 + */ + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + private String menuName; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空") + private Integer orderNum; + + /** + * 路由地址 + */ + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + private String path; + + /** + * 组件路径 + */ + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + private String component; + + /** + * 路由参数 + */ + private String queryParam; + + /** + * 是否为外链(0是 1否) + */ + private String isFrame; + + /** + * 是否缓存(0缓存 1不缓存) + */ + private String isCache; + + /** + * 类型(M目录 C菜单 F按钮) + */ + @NotBlank(message = "菜单类型不能为空") + private String menuType; + + /** + * 显示状态(0显示 1隐藏) + */ + private String visible; + + /** + * 菜单状态(0正常 1停用) + */ + private String status; + + /** + * 权限字符串 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + private String perms; + + /** + * 菜单图标 + */ + private String icon; + + /** + * 备注 + */ + private String remark; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/entity/SysRole.java b/service/src/main/java/com/rzdata/common/core/domain/entity/SysRole.java new file mode 100644 index 0000000..f30326e --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/entity/SysRole.java @@ -0,0 +1,131 @@ +package com.rzdata.common.core.domain.entity; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.convert.ExcelDictConvert; +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.Set; + +/** + * 角色表 sys_role + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_role") +@ExcelIgnoreUnannotated +public class SysRole extends BaseEntity { + + /** + * 角色ID + */ + @ExcelProperty(value = "角色序号") + @TableId(value = "role_id") + private Long roleId; + + /** + * 角色名称 + */ + @ExcelProperty(value = "角色名称") + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + private String roleName; + + /** + * 角色权限 + */ + @ExcelProperty(value = "角色权限") + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + private String roleKey; + + /** + * 角色排序 + */ + @ExcelProperty(value = "角色排序") + @NotNull(message = "显示顺序不能为空") + private Integer roleSort; + + /** + * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) + */ + @ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** + * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) + */ + private Boolean menuCheckStrictly; + + /** + * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) + */ + private Boolean deptCheckStrictly; + + /** + * 角色状态(0正常 1停用) + */ + @ExcelProperty(value = "角色状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 备注 + */ + private String remark; + + /** + * 用户是否存在此角色标识 默认不存在 + */ + @TableField(exist = false) + private boolean flag = false; + + /** + * 菜单组 + */ + @TableField(exist = false) + private Long[] menuIds; + + /** + * 部门组(数据权限) + */ + @TableField(exist = false) + private Long[] deptIds; + + /** + * 角色菜单权限 + */ + @TableField(exist = false) + private Set permissions; + + public SysRole(Long roleId) { + this.roleId = roleId; + } + + public boolean isAdmin() { + return UserConstants.ADMIN_ID.equals(this.roleId); + } +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/entity/SysUser.java b/service/src/main/java/com/rzdata/common/core/domain/entity/SysUser.java new file mode 100644 index 0000000..c95cd5e --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/entity/SysUser.java @@ -0,0 +1,160 @@ +package com.rzdata.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.rzdata.common.annotation.Sensitive; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.BaseEntity; +import com.rzdata.common.enums.SensitiveStrategy; +import com.rzdata.common.xss.Xss; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.util.Date; +import java.util.List; + +/** + * 用户对象 sys_user + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_user") +public class SysUser extends BaseEntity { + + /** + * 用户ID + */ + @TableId(value = "user_id") + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + private String userName; + + /** + * 用户昵称 + */ + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + @Sensitive(strategy = SensitiveStrategy.EMAIL) + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + private String email; + + /** + * 手机号码 + */ + @Sensitive(strategy = SensitiveStrategy.PHONE) + private String phonenumber; + + /** + * 用户性别 + */ + private String sex; + + /** + * 用户头像 + */ + private String avatar; + + /** + * 密码 + */ + @TableField( + insertStrategy = FieldStrategy.NOT_EMPTY, + updateStrategy = FieldStrategy.NOT_EMPTY, + whereStrategy = FieldStrategy.NOT_EMPTY + ) + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 最后登录IP + */ + private String loginIp; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 备注 + */ + private String remark; + + /** + * 部门对象 + */ + @TableField(exist = false) + private SysDept dept; + + /** + * 角色对象 + */ + @TableField(exist = false) + private List roles; + + /** + * 角色组 + */ + @TableField(exist = false) + private Long[] roleIds; + + /** + * 岗位组 + */ + @TableField(exist = false) + private Long[] postIds; + + /** + * 数据权限 当前角色ID + */ + @TableField(exist = false) + private Long roleId; + + public SysUser(Long userId) { + this.userId = userId; + } + + public boolean isAdmin() { + return UserConstants.ADMIN_ID.equals(this.userId); + } + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/event/LogininforEvent.java b/service/src/main/java/com/rzdata/common/core/domain/event/LogininforEvent.java new file mode 100644 index 0000000..58e8acb --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/event/LogininforEvent.java @@ -0,0 +1,44 @@ +package com.rzdata.common.core.domain.event; + +import lombok.Data; + +import javax.servlet.http.HttpServletRequest; +import java.io.Serializable; + +/** + * 登录事件 + * + * @author Lion Li + */ + +@Data +public class LogininforEvent implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户账号 + */ + private String username; + + /** + * 登录状态 0成功 1失败 + */ + private String status; + + /** + * 提示消息 + */ + private String message; + + /** + * 请求体 + */ + private HttpServletRequest request; + + /** + * 其他参数 + */ + private Object[] args; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/event/OperLogEvent.java b/service/src/main/java/com/rzdata/common/core/domain/event/OperLogEvent.java new file mode 100644 index 0000000..2e35bfe --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/event/OperLogEvent.java @@ -0,0 +1,104 @@ +package com.rzdata.common.core.domain.event; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志事件 + * + * @author Lion Li + */ + +@Data +public class OperLogEvent implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + private Long operId; + + /** + * 操作模块 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 请求方法 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 操作地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/model/LoginBody.java b/service/src/main/java/com/rzdata/common/core/domain/model/LoginBody.java new file mode 100644 index 0000000..c4976ed --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/model/LoginBody.java @@ -0,0 +1,42 @@ +package com.rzdata.common.core.domain.model; + +import com.rzdata.common.constant.UserConstants; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; + +/** + * 用户登录对象 + * + * @author Lion Li + */ + +@Data +public class LoginBody { + + /** + * 用户名 + */ + @NotBlank(message = "{user.username.not.blank}") + @Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = "{user.username.length.valid}") + private String username; + + /** + * 用户密码 + */ + @NotBlank(message = "{user.password.not.blank}") + @Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}") + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/model/LoginUser.java b/service/src/main/java/com/rzdata/common/core/domain/model/LoginUser.java new file mode 100644 index 0000000..acc3576 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/model/LoginUser.java @@ -0,0 +1,117 @@ +package com.rzdata.common.core.domain.model; + +import com.rzdata.common.core.domain.dto.RoleDTO; +import com.rzdata.common.helper.LoginHelper; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class LoginUser implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门名 + */ + private String deptName; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户类型 + */ + private String userType; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 菜单权限 + */ + private Set menuPermission; + + /** + * 角色权限 + */ + private Set rolePermission; + + /** + * 用户名 + */ + private String username; + + /** + * 角色对象 + */ + private List roles; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + /** + * 获取登录id + */ + public String getLoginId() { + if (userType == null) { + throw new IllegalArgumentException("用户类型不能为空"); + } + if (userId == null) { + throw new IllegalArgumentException("用户ID不能为空"); + } + return userType + LoginHelper.JOIN_CODE + userId; + } + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/model/RegisterBody.java b/service/src/main/java/com/rzdata/common/core/domain/model/RegisterBody.java new file mode 100644 index 0000000..7306592 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/model/RegisterBody.java @@ -0,0 +1,17 @@ +package com.rzdata.common.core.domain.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 用户注册对象 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class RegisterBody extends LoginBody { + + private String userType; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/model/SmsLoginBody.java b/service/src/main/java/com/rzdata/common/core/domain/model/SmsLoginBody.java new file mode 100644 index 0000000..468e75a --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/model/SmsLoginBody.java @@ -0,0 +1,28 @@ +package com.rzdata.common.core.domain.model; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * 短信登录对象 + * + * @author Lion Li + */ + +@Data +public class SmsLoginBody { + + /** + * 用户名 + */ + @NotBlank(message = "{user.phonenumber.not.blank}") + private String phonenumber; + + /** + * 用户密码 + */ + @NotBlank(message = "{sms.code.not.blank}") + private String smsCode; + +} diff --git a/service/src/main/java/com/rzdata/common/core/domain/model/XcxLoginUser.java b/service/src/main/java/com/rzdata/common/core/domain/model/XcxLoginUser.java new file mode 100644 index 0000000..36fe298 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/domain/model/XcxLoginUser.java @@ -0,0 +1,24 @@ +package com.rzdata.common.core.domain.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 小程序登录用户身份权限 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class XcxLoginUser extends LoginUser { + + private static final long serialVersionUID = 1L; + + /** + * openid + */ + private String openid; + +} diff --git a/service/src/main/java/com/rzdata/common/core/mapper/BaseMapperPlus.java b/service/src/main/java/com/rzdata/common/core/mapper/BaseMapperPlus.java new file mode 100644 index 0000000..4936b81 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/mapper/BaseMapperPlus.java @@ -0,0 +1,192 @@ +package com.rzdata.common.core.mapper; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.*; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.toolkit.Db; +import com.rzdata.common.utils.BeanCopyUtils; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 自定义 Mapper 接口, 实现 自定义扩展 + * + * @param mapper 泛型 + * @param table 泛型 + * @param vo 泛型 + * @author Lion Li + * @since 2021-05-13 + */ +@SuppressWarnings("unchecked") +public interface BaseMapperPlus extends BaseMapper { + + Log log = LogFactory.getLog(BaseMapperPlus.class); + + default Class currentVoClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2); + } + + default Class currentModelClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1); + } + + default Class currentMapperClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0); + } + + default List selectList() { + return this.selectList(new QueryWrapper<>()); + } + + /** + * 批量插入 + */ + default boolean insertBatch(Collection entityList) { + return Db.saveBatch(entityList); + } + + /** + * 批量更新 + */ + default boolean updateBatchById(Collection entityList) { + return Db.updateBatchById(entityList); + } + + /** + * 批量插入或更新 + */ + default boolean insertOrUpdateBatch(Collection entityList) { + return Db.saveOrUpdateBatch(entityList); + } + + /** + * 批量插入(包含限制条数) + */ + default boolean insertBatch(Collection entityList, int batchSize) { + return Db.saveBatch(entityList, batchSize); + } + + /** + * 批量更新(包含限制条数) + */ + default boolean updateBatchById(Collection entityList, int batchSize) { + return Db.updateBatchById(entityList, batchSize); + } + + /** + * 批量插入或更新(包含限制条数) + */ + default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { + return Db.saveOrUpdateBatch(entityList, batchSize); + } + + /** + * 插入或更新(包含限制条数) + */ + default boolean insertOrUpdate(T entity) { + return Db.saveOrUpdate(entity); + } + + default V selectVoById(Serializable id) { + return selectVoById(id, this.currentVoClass()); + } + + /** + * 根据 ID 查询 + */ + default C selectVoById(Serializable id, Class voClass) { + T obj = this.selectById(id); + if (ObjectUtil.isNull(obj)) { + return null; + } + return BeanCopyUtils.copy(obj, voClass); + } + + default List selectVoBatchIds(Collection idList) { + return selectVoBatchIds(idList, this.currentVoClass()); + } + + /** + * 查询(根据ID 批量查询) + */ + default List selectVoBatchIds(Collection idList, Class voClass) { + List list = this.selectBatchIds(idList); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return BeanCopyUtils.copyList(list, voClass); + } + + default List selectVoByMap(Map map) { + return selectVoByMap(map, this.currentVoClass()); + } + + /** + * 查询(根据 columnMap 条件) + */ + default List selectVoByMap(Map map, Class voClass) { + List list = this.selectByMap(map); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return BeanCopyUtils.copyList(list, voClass); + } + + default V selectVoOne(Wrapper wrapper) { + return selectVoOne(wrapper, this.currentVoClass()); + } + + /** + * 根据 entity 条件,查询一条记录 + */ + default C selectVoOne(Wrapper wrapper, Class voClass) { + T obj = this.selectOne(wrapper); + if (ObjectUtil.isNull(obj)) { + return null; + } + return BeanCopyUtils.copy(obj, voClass); + } + + default List selectVoList(Wrapper wrapper) { + return selectVoList(wrapper, this.currentVoClass()); + } + + /** + * 根据 entity 条件,查询全部记录 + */ + default List selectVoList(Wrapper wrapper, Class voClass) { + List list = this.selectList(wrapper); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return BeanCopyUtils.copyList(list, voClass); + } + + default

> P selectVoPage(IPage page, Wrapper wrapper) { + return selectVoPage(page, wrapper, this.currentVoClass()); + } + + /** + * 分页查询VO + */ + default > P selectVoPage(IPage page, Wrapper wrapper, Class voClass) { + IPage pageData = this.selectPage(page, wrapper); + IPage voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()); + if (CollUtil.isEmpty(pageData.getRecords())) { + return (P) voPage; + } + voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass)); + return (P) voPage; + } + +} diff --git a/service/src/main/java/com/rzdata/common/core/page/TableDataInfo.java b/service/src/main/java/com/rzdata/common/core/page/TableDataInfo.java new file mode 100644 index 0000000..9bfc4e7 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/page/TableDataInfo.java @@ -0,0 +1,78 @@ +package com.rzdata.common.core.page; + +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.metadata.IPage; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class TableDataInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + /** + * 消息状态码 + */ + private int code; + + /** + * 消息内容 + */ + private String msg; + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, long total) { + this.rows = list; + this.total = total; + } + + public static TableDataInfo build(IPage page) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + public static TableDataInfo build(List list) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(list.size()); + return rspData; + } + + public static TableDataInfo build() { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + return rspData; + } + +} diff --git a/service/src/main/java/com/rzdata/common/core/service/ConfigService.java b/service/src/main/java/com/rzdata/common/core/service/ConfigService.java new file mode 100644 index 0000000..51f50f2 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/service/ConfigService.java @@ -0,0 +1,18 @@ +package com.rzdata.common.core.service; + +/** + * 通用 参数配置服务 + * + * @author Lion Li + */ +public interface ConfigService { + + /** + * 根据参数 key 获取参数值 + * + * @param configKey 参数 key + * @return 参数值 + */ + String getConfigValue(String configKey); + +} diff --git a/service/src/main/java/com/rzdata/common/core/service/DictService.java b/service/src/main/java/com/rzdata/common/core/service/DictService.java new file mode 100644 index 0000000..1fd1216 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/service/DictService.java @@ -0,0 +1,57 @@ +package com.rzdata.common.core.service; + +/** + * 通用 字典服务 + * + * @author Lion Li + */ +public interface DictService { + + /** + * 分隔符 + */ + String SEPARATOR = ","; + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + default String getDictLabel(String dictType, String dictValue) { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + default String getDictValue(String dictType, String dictLabel) { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + String getDictLabel(String dictType, String dictValue, String separator); + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + String getDictValue(String dictType, String dictLabel, String separator); + +} diff --git a/service/src/main/java/com/rzdata/common/core/service/SensitiveService.java b/service/src/main/java/com/rzdata/common/core/service/SensitiveService.java new file mode 100644 index 0000000..31e8bf1 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/service/SensitiveService.java @@ -0,0 +1,18 @@ +package com.rzdata.common.core.service; + +/** + * 脱敏服务 + * 默认管理员不过滤 + * 需自行根据业务重写实现 + * + * @author Lion Li + * @version 3.6.0 + */ +public interface SensitiveService { + + /** + * 是否脱敏 + */ + boolean isSensitive(); + +} diff --git a/service/src/main/java/com/rzdata/common/core/validate/AddGroup.java b/service/src/main/java/com/rzdata/common/core/validate/AddGroup.java new file mode 100644 index 0000000..4f3a9b8 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/validate/AddGroup.java @@ -0,0 +1,9 @@ +package com.rzdata.common.core.validate; + +/** + * 校验分组 add + * + * @author Lion Li + */ +public interface AddGroup { +} diff --git a/service/src/main/java/com/rzdata/common/core/validate/EditGroup.java b/service/src/main/java/com/rzdata/common/core/validate/EditGroup.java new file mode 100644 index 0000000..79f3323 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/validate/EditGroup.java @@ -0,0 +1,9 @@ +package com.rzdata.common.core.validate; + +/** + * 校验分组 edit + * + * @author Lion Li + */ +public interface EditGroup { +} diff --git a/service/src/main/java/com/rzdata/common/core/validate/QueryGroup.java b/service/src/main/java/com/rzdata/common/core/validate/QueryGroup.java new file mode 100644 index 0000000..ccaeefa --- /dev/null +++ b/service/src/main/java/com/rzdata/common/core/validate/QueryGroup.java @@ -0,0 +1,9 @@ +package com.rzdata.common.core.validate; + +/** + * 校验分组 query + * + * @author Lion Li + */ +public interface QueryGroup { +} diff --git a/service/src/main/java/com/rzdata/common/enums/BusinessStatus.java b/service/src/main/java/com/rzdata/common/enums/BusinessStatus.java new file mode 100644 index 0000000..1dd5ae1 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/BusinessStatus.java @@ -0,0 +1,18 @@ +package com.rzdata.common.enums; + +/** + * 操作状态 + * + * @author rzdata + */ +public enum BusinessStatus { + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/service/src/main/java/com/rzdata/common/enums/BusinessType.java b/service/src/main/java/com/rzdata/common/enums/BusinessType.java new file mode 100644 index 0000000..65782e5 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/BusinessType.java @@ -0,0 +1,58 @@ +package com.rzdata.common.enums; + +/** + * 业务操作类型 + * + * @author rzdata + */ +public enum BusinessType { + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/service/src/main/java/com/rzdata/common/enums/CaptchaCategory.java b/service/src/main/java/com/rzdata/common/enums/CaptchaCategory.java new file mode 100644 index 0000000..676b423 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/CaptchaCategory.java @@ -0,0 +1,35 @@ +package com.rzdata.common.enums; + +import cn.hutool.captcha.AbstractCaptcha; +import cn.hutool.captcha.CircleCaptcha; +import cn.hutool.captcha.LineCaptcha; +import cn.hutool.captcha.ShearCaptcha; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 验证码类别 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum CaptchaCategory { + + /** + * 线段干扰 + */ + LINE(LineCaptcha.class), + + /** + * 圆圈干扰 + */ + CIRCLE(CircleCaptcha.class), + + /** + * 扭曲干扰 + */ + SHEAR(ShearCaptcha.class); + + private final Class clazz; +} diff --git a/service/src/main/java/com/rzdata/common/enums/CaptchaType.java b/service/src/main/java/com/rzdata/common/enums/CaptchaType.java new file mode 100644 index 0000000..1678dc4 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/CaptchaType.java @@ -0,0 +1,29 @@ +package com.rzdata.common.enums; + +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.captcha.generator.RandomGenerator; +import com.rzdata.common.captcha.UnsignedMathGenerator; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 验证码类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum CaptchaType { + + /** + * 数字 + */ + MATH(UnsignedMathGenerator.class), + + /** + * 字符 + */ + CHAR(RandomGenerator.class); + + private final Class clazz; +} diff --git a/service/src/main/java/com/rzdata/common/enums/DataBaseType.java b/service/src/main/java/com/rzdata/common/enums/DataBaseType.java new file mode 100644 index 0000000..df15a1e --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/DataBaseType.java @@ -0,0 +1,49 @@ +package com.rzdata.common.enums; + +import com.rzdata.common.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据库类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum DataBaseType { + + /** + * MySQL + */ + MY_SQL("MySQL"), + + /** + * Oracle + */ + ORACLE("Oracle"), + + /** + * PostgreSQL + */ + POSTGRE_SQL("PostgreSQL"), + + /** + * SQL Server + */ + SQL_SERVER("Microsoft SQL Server"); + + private final String type; + + public static DataBaseType find(String databaseProductName) { + if (StringUtils.isBlank(databaseProductName)) { + return null; + } + for (DataBaseType type : values()) { + if (type.getType().equals(databaseProductName)) { + return type; + } + } + return null; + } +} diff --git a/service/src/main/java/com/rzdata/common/enums/DataScopeType.java b/service/src/main/java/com/rzdata/common/enums/DataScopeType.java new file mode 100644 index 0000000..9180bd6 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/DataScopeType.java @@ -0,0 +1,72 @@ +package com.rzdata.common.enums; + +import com.rzdata.common.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据权限类型 + *

+ * 语法支持 spel 模板表达式 + *

+ * 内置数据 user 当前用户 内容参考 LoginUser + * 如需扩展数据 可使用 {@link com.rzdata.common.helper.DataPermissionHelper} 操作 + * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService + * 如需扩展更多自定义服务 可以参考 sdss 自行编写 + * + * @author Lion Li + * @version 3.5.0 + */ +@Getter +@AllArgsConstructor +public enum DataScopeType { + + /** + * 全部数据权限 + */ + ALL("1", "", ""), + + /** + * 自定数据权限 + */ + CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", ""), + + /** + * 部门数据权限 + */ + DEPT("3", " #{#deptName} = #{#user.deptId} ", ""), + + /** + * 部门及以下数据权限 + */ + DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", ""), + + /** + * 仅本人数据权限 + */ + SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "); + + private final String code; + + /** + * 语法 采用 spel 模板表达式 + */ + private final String sqlTemplate; + + /** + * 不满足 sqlTemplate 则填充 + */ + private final String elseSql; + + public static DataScopeType findCode(String code) { + if (StringUtils.isBlank(code)) { + return null; + } + for (DataScopeType type : values()) { + if (type.getCode().equals(code)) { + return type; + } + } + return null; + } +} diff --git a/service/src/main/java/com/rzdata/common/enums/DeviceType.java b/service/src/main/java/com/rzdata/common/enums/DeviceType.java new file mode 100644 index 0000000..ca4e1af --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/DeviceType.java @@ -0,0 +1,32 @@ +package com.rzdata.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备类型 + * 针对一套 用户体系 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum DeviceType { + + /** + * pc端 + */ + PC("pc"), + + /** + * app端 + */ + APP("app"), + + /** + * 小程序端 + */ + XCX("xcx"); + + private final String device; +} diff --git a/service/src/main/java/com/rzdata/common/enums/HttpMethod.java b/service/src/main/java/com/rzdata/common/enums/HttpMethod.java new file mode 100644 index 0000000..56e4bfc --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/HttpMethod.java @@ -0,0 +1,32 @@ +package com.rzdata.common.enums; + +import org.springframework.lang.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * 请求方式 + * + * @author rzdata + */ +public enum HttpMethod { + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static { + for (HttpMethod httpMethod : values()) { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) { + return (this == resolve(method)); + } +} diff --git a/service/src/main/java/com/rzdata/common/enums/LimitType.java b/service/src/main/java/com/rzdata/common/enums/LimitType.java new file mode 100644 index 0000000..a156896 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/LimitType.java @@ -0,0 +1,24 @@ +package com.rzdata.common.enums; + +/** + * 限流类型 + * + * @author rzdata + */ + +public enum LimitType { + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP, + + /** + * 实例限流(集群多后端实例) + */ + CLUSTER +} diff --git a/service/src/main/java/com/rzdata/common/enums/LoginType.java b/service/src/main/java/com/rzdata/common/enums/LoginType.java new file mode 100644 index 0000000..5b5aebb --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/LoginType.java @@ -0,0 +1,39 @@ +package com.rzdata.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 登录类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum LoginType { + + /** + * 密码登录 + */ + PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"), + + /** + * 短信登录 + */ + SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), + + /** + * 小程序登录 + */ + XCX("", ""); + + /** + * 登录重试超出限制提示 + */ + final String retryLimitExceed; + + /** + * 登录重试限制计数提示 + */ + final String retryLimitCount; +} diff --git a/service/src/main/java/com/rzdata/common/enums/OperatorType.java b/service/src/main/java/com/rzdata/common/enums/OperatorType.java new file mode 100644 index 0000000..b7f62cd --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/OperatorType.java @@ -0,0 +1,23 @@ +package com.rzdata.common.enums; + +/** + * 操作人类别 + * + * @author rzdata + */ +public enum OperatorType { + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/service/src/main/java/com/rzdata/common/enums/SensitiveStrategy.java b/service/src/main/java/com/rzdata/common/enums/SensitiveStrategy.java new file mode 100644 index 0000000..24ad864 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/SensitiveStrategy.java @@ -0,0 +1,49 @@ +package com.rzdata.common.enums; + +import cn.hutool.core.util.DesensitizedUtil; +import lombok.AllArgsConstructor; + +import java.util.function.Function; + +/** + * 脱敏策略 + * + * @author Yjoioooo + * @version 3.6.0 + */ +@AllArgsConstructor +public enum SensitiveStrategy { + + /** + * 身份证脱敏 + */ + ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)), + + /** + * 手机号脱敏 + */ + PHONE(DesensitizedUtil::mobilePhone), + + /** + * 地址脱敏 + */ + ADDRESS(s -> DesensitizedUtil.address(s, 8)), + + /** + * 邮箱脱敏 + */ + EMAIL(DesensitizedUtil::email), + + /** + * 银行卡 + */ + BANK_CARD(DesensitizedUtil::bankCard); + + //可自行添加其他脱敏策略 + + private final Function desensitizer; + + public Function desensitizer() { + return desensitizer; + } +} diff --git a/service/src/main/java/com/rzdata/common/enums/UserStatus.java b/service/src/main/java/com/rzdata/common/enums/UserStatus.java new file mode 100644 index 0000000..19249c7 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/UserStatus.java @@ -0,0 +1,26 @@ +package com.rzdata.common.enums; + +/** + * 用户状态 + * + * @author rzdata + */ +public enum UserStatus { + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/service/src/main/java/com/rzdata/common/enums/UserType.java b/service/src/main/java/com/rzdata/common/enums/UserType.java new file mode 100644 index 0000000..0684695 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/enums/UserType.java @@ -0,0 +1,37 @@ +package com.rzdata.common.enums; + +import com.rzdata.common.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备类型 + * 针对多套 用户体系 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum UserType { + + /** + * pc端 + */ + SYS_USER("sys_user"), + + /** + * app端 + */ + APP_USER("app_user"); + + private final String userType; + + public static UserType getUserType(String str) { + for (UserType value : values()) { + if (StringUtils.contains(str, value.getUserType())) { + return value; + } + } + throw new RuntimeException("'UserType' not found By " + str); + } +} diff --git a/service/src/main/java/com/rzdata/common/excel/CellMergeStrategy.java b/service/src/main/java/com/rzdata/common/excel/CellMergeStrategy.java new file mode 100644 index 0000000..aeb6dfe --- /dev/null +++ b/service/src/main/java/com/rzdata/common/excel/CellMergeStrategy.java @@ -0,0 +1,114 @@ +package com.rzdata.common.excel; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.merge.AbstractMergeStrategy; +import com.rzdata.common.annotation.CellMerge; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 列值重复合并策略 + * + * @author Lion Li + */ +@AllArgsConstructor +@Slf4j +public class CellMergeStrategy extends AbstractMergeStrategy { + + private List list; + private boolean hasTitle; + + @Override + protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { + List cellList = handle(list, hasTitle); + // judge the list is not null + if (CollectionUtils.isNotEmpty(cellList)) { + // the judge is necessary + if (cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) { + for (CellRangeAddress item : cellList) { + sheet.addMergedRegion(item); + } + } + } + } + + @SneakyThrows + private static List handle(List list, boolean hasTitle) { + List cellList = new ArrayList<>(); + if (CollectionUtils.isEmpty(list)) { + return cellList; + } + Class clazz = list.get(0).getClass(); + Field[] fields = clazz.getDeclaredFields(); + // 有注解的字段 + List mergeFields = new ArrayList<>(); + List mergeFieldsIndex = new ArrayList<>(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if (field.isAnnotationPresent(CellMerge.class)) { + CellMerge cm = field.getAnnotation(CellMerge.class); + mergeFields.add(field); + mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index()); + } + } + // 行合并开始下标 + int rowIndex = hasTitle ? 1 : 0; + Map map = new HashMap<>(); + // 生成两两合并单元格 + for (int i = 0; i < list.size(); i++) { + for (int j = 0; j < mergeFields.size(); j++) { + Field field = mergeFields.get(j); + String name = field.getName(); + String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); + Method readMethod = clazz.getMethod(methodName); + Object val = readMethod.invoke(list.get(i)); + + int colNum = mergeFieldsIndex.get(j); + if (!map.containsKey(field)) { + map.put(field, new RepeatCell(val, i)); + } else { + RepeatCell repeatCell = map.get(field); + Object cellValue = repeatCell.getValue(); + if (cellValue == null || "".equals(cellValue)) { + // 空值跳过不合并 + continue; + } + if (!cellValue.equals(val)) { + if (i - repeatCell.getCurrent() > 1) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + map.put(field, new RepeatCell(val, i)); + } else if (i == list.size() - 1) { + if (i > repeatCell.getCurrent()) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); + } + } + } + } + } + return cellList; + } + + @Data + @AllArgsConstructor + static class RepeatCell { + + private Object value; + + private int current; + + } +} diff --git a/service/src/main/java/com/rzdata/common/excel/DefaultExcelListener.java b/service/src/main/java/com/rzdata/common/excel/DefaultExcelListener.java new file mode 100644 index 0000000..95ad20d --- /dev/null +++ b/service/src/main/java/com/rzdata/common/excel/DefaultExcelListener.java @@ -0,0 +1,106 @@ +package com.rzdata.common.excel; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.common.utils.ValidatorUtils; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.util.Map; +import java.util.Set; + +/** + * Excel 导入监听 + * + * @author Yjoioooo + * @author Lion Li + */ +@Slf4j +@NoArgsConstructor +public class DefaultExcelListener extends AnalysisEventListener implements ExcelListener { + + /** + * 是否Validator检验,默认为是 + */ + private Boolean isValidate = Boolean.TRUE; + + /** + * excel 表头数据 + */ + private Map headMap; + + /** + * 导入回执 + */ + private ExcelResult excelResult; + + public DefaultExcelListener(boolean isValidate) { + this.excelResult = new DefautExcelResult<>(); + this.isValidate = isValidate; + } + + /** + * 处理异常 + * + * @param exception ExcelDataConvertException + * @param context Excel 上下文 + */ + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + String errMsg = null; + if (exception instanceof ExcelDataConvertException) { + // 如果是某一个单元格的转换异常 能获取到具体行号 + ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception; + Integer rowIndex = excelDataConvertException.getRowIndex(); + Integer columnIndex = excelDataConvertException.getColumnIndex(); + errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常
", + rowIndex + 1, columnIndex + 1, headMap.get(columnIndex)); + if (log.isDebugEnabled()) { + log.error(errMsg); + } + } + if (exception instanceof ConstraintViolationException) { + ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception; + Set> constraintViolations = constraintViolationException.getConstraintViolations(); + String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", "); + errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg); + if (log.isDebugEnabled()) { + log.error(errMsg); + } + } + excelResult.getErrorList().add(errMsg); + throw new ExcelAnalysisException(errMsg); + } + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + this.headMap = headMap; + log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap)); + } + + @Override + public void invoke(T data, AnalysisContext context) { + if (isValidate) { + ValidatorUtils.validate(data); + } + excelResult.getList().add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.debug("所有数据解析完成!"); + } + + @Override + public ExcelResult getExcelResult() { + return excelResult; + } + +} diff --git a/service/src/main/java/com/rzdata/common/excel/DefautExcelResult.java b/service/src/main/java/com/rzdata/common/excel/DefautExcelResult.java new file mode 100644 index 0000000..7e4f7bf --- /dev/null +++ b/service/src/main/java/com/rzdata/common/excel/DefautExcelResult.java @@ -0,0 +1,73 @@ +package com.rzdata.common.excel; + +import cn.hutool.core.util.StrUtil; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * 默认excel返回对象 + * + * @author Yjoioooo + * @author Lion Li + */ +public class DefautExcelResult implements ExcelResult { + + /** + * 数据对象list + */ + @Setter + private List list; + + /** + * 错误信息列表 + */ + @Setter + private List errorList; + + public DefautExcelResult() { + this.list = new ArrayList<>(); + this.errorList = new ArrayList<>(); + } + + public DefautExcelResult(List list, List errorList) { + this.list = list; + this.errorList = errorList; + } + + public DefautExcelResult(ExcelResult excelResult) { + this.list = excelResult.getList(); + this.errorList = excelResult.getErrorList(); + } + + @Override + public List getList() { + return list; + } + + @Override + public List getErrorList() { + return errorList; + } + + /** + * 获取导入回执 + * + * @return 导入回执 + */ + @Override + public String getAnalysis() { + int successCount = list.size(); + int errorCount = errorList.size(); + if (successCount == 0) { + return "读取失败,未解析到数据"; + } else { + if (errorCount == 0) { + return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount); + } else { + return ""; + } + } + } +} diff --git a/service/src/main/java/com/rzdata/common/excel/ExcelListener.java b/service/src/main/java/com/rzdata/common/excel/ExcelListener.java new file mode 100644 index 0000000..55a692c --- /dev/null +++ b/service/src/main/java/com/rzdata/common/excel/ExcelListener.java @@ -0,0 +1,14 @@ +package com.rzdata.common.excel; + +import com.alibaba.excel.read.listener.ReadListener; + +/** + * Excel 导入监听 + * + * @author Lion Li + */ +public interface ExcelListener extends ReadListener { + + ExcelResult getExcelResult(); + +} diff --git a/service/src/main/java/com/rzdata/common/excel/ExcelResult.java b/service/src/main/java/com/rzdata/common/excel/ExcelResult.java new file mode 100644 index 0000000..5bf5df4 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/excel/ExcelResult.java @@ -0,0 +1,26 @@ +package com.rzdata.common.excel; + +import java.util.List; + +/** + * excel返回对象 + * + * @author Lion Li + */ +public interface ExcelResult { + + /** + * 对象列表 + */ + List getList(); + + /** + * 错误列表 + */ + List getErrorList(); + + /** + * 导入回执 + */ + String getAnalysis(); +} diff --git a/service/src/main/java/com/rzdata/common/exception/DemoModeException.java b/service/src/main/java/com/rzdata/common/exception/DemoModeException.java new file mode 100644 index 0000000..b0295d5 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/DemoModeException.java @@ -0,0 +1,13 @@ +package com.rzdata.common.exception; + +/** + * 演示模式异常 + * + * @author rzdata + */ +public class DemoModeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public DemoModeException() { + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/GlobalException.java b/service/src/main/java/com/rzdata/common/exception/GlobalException.java new file mode 100644 index 0000000..7633901 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/GlobalException.java @@ -0,0 +1,52 @@ +package com.rzdata.common.exception; + +/** + * 全局异常 + * + * @author rzdata + */ +public class GlobalException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + *

+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() { + } + + public GlobalException(String message) { + this.message = message; + } + + public String getDetailMessage() { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage() { + return message; + } + + public GlobalException setMessage(String message) { + this.message = message; + return this; + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/ServiceException.java b/service/src/main/java/com/rzdata/common/exception/ServiceException.java new file mode 100644 index 0000000..cd5bbdd --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/ServiceException.java @@ -0,0 +1,65 @@ +package com.rzdata.common.exception; + +/** + * 业务异常 + * + * @author rzdata + */ +public final class ServiceException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + *

+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() { + } + + public ServiceException(String message) { + this.message = message; + } + + public ServiceException(String message, Integer code) { + this.message = message; + this.code = code; + } + + public String getDetailMessage() { + return detailMessage; + } + + @Override + public String getMessage() { + return message; + } + + public Integer getCode() { + return code; + } + + public ServiceException setMessage(String message) { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/UtilException.java b/service/src/main/java/com/rzdata/common/exception/UtilException.java new file mode 100644 index 0000000..b7cd683 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/UtilException.java @@ -0,0 +1,22 @@ +package com.rzdata.common.exception; + +/** + * 工具类异常 + * + * @author rzdata + */ +public class UtilException extends RuntimeException { + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) { + super(e.getMessage(), e); + } + + public UtilException(String message) { + super(message); + } + + public UtilException(String message, Throwable throwable) { + super(message, throwable); + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/base/BaseException.java b/service/src/main/java/com/rzdata/common/exception/base/BaseException.java new file mode 100644 index 0000000..bd5c703 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/base/BaseException.java @@ -0,0 +1,75 @@ +package com.rzdata.common.exception.base; + +import com.rzdata.common.utils.MessageUtils; +import com.rzdata.common.utils.StringUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 基础异常 + * + * @author rzdata + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class BaseException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() { + String message = null; + if (!StringUtils.isEmpty(code)) { + message = MessageUtils.message(code, args); + } + if (message == null) { + message = defaultMessage; + } + return message; + } + +} diff --git a/service/src/main/java/com/rzdata/common/exception/file/FileException.java b/service/src/main/java/com/rzdata/common/exception/file/FileException.java new file mode 100644 index 0000000..faeea39 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/file/FileException.java @@ -0,0 +1,17 @@ +package com.rzdata.common.exception.file; + +import com.rzdata.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author rzdata + */ +public class FileException extends BaseException { + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) { + super("file", code, args, null); + } + +} diff --git a/service/src/main/java/com/rzdata/common/exception/file/FileNameLengthLimitExceededException.java b/service/src/main/java/com/rzdata/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..cc35084 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,14 @@ +package com.rzdata.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author rzdata + */ +public class FileNameLengthLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) { + super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}); + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/file/FileSizeLimitExceededException.java b/service/src/main/java/com/rzdata/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..58f8815 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,14 @@ +package com.rzdata.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author rzdata + */ +public class FileSizeLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) { + super("upload.exceed.maxSize", new Object[]{defaultMaxSize}); + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/user/CaptchaException.java b/service/src/main/java/com/rzdata/common/exception/user/CaptchaException.java new file mode 100644 index 0000000..5c8e659 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/user/CaptchaException.java @@ -0,0 +1,14 @@ +package com.rzdata.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author rzdata + */ +public class CaptchaException extends UserException { + private static final long serialVersionUID = 1L; + + public CaptchaException() { + super("user.jcaptcha.error"); + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/user/CaptchaExpireException.java b/service/src/main/java/com/rzdata/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..0d6f23b --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,14 @@ +package com.rzdata.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author rzdata + */ +public class CaptchaExpireException extends UserException { + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() { + super("user.jcaptcha.expire"); + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/user/UserException.java b/service/src/main/java/com/rzdata/common/exception/user/UserException.java new file mode 100644 index 0000000..9a9b653 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/user/UserException.java @@ -0,0 +1,16 @@ +package com.rzdata.common.exception.user; + +import com.rzdata.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author rzdata + */ +public class UserException extends BaseException { + private static final long serialVersionUID = 1L; + + public UserException(String code, Object... args) { + super("user", code, args, null); + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/user/UserPasswordNotMatchException.java b/service/src/main/java/com/rzdata/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..3956622 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,14 @@ +package com.rzdata.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author rzdata + */ +public class UserPasswordNotMatchException extends UserException { + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() { + super("user.password.not.match"); + } +} diff --git a/service/src/main/java/com/rzdata/common/exception/user/UserPasswordRetryLimitExceedException.java b/service/src/main/java/com/rzdata/common/exception/user/UserPasswordRetryLimitExceedException.java new file mode 100644 index 0000000..3df26fd --- /dev/null +++ b/service/src/main/java/com/rzdata/common/exception/user/UserPasswordRetryLimitExceedException.java @@ -0,0 +1,16 @@ +package com.rzdata.common.exception.user; + +/** + * 用户错误最大次数异常类 + * + * @author rzdata + */ +public class UserPasswordRetryLimitExceedException extends UserException { + + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) { + super("user.password.retry.limit.exceed", retryLimitCount, lockTime); + } + +} diff --git a/service/src/main/java/com/rzdata/common/filter/RepeatableFilter.java b/service/src/main/java/com/rzdata/common/filter/RepeatableFilter.java new file mode 100644 index 0000000..5fcd136 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/filter/RepeatableFilter.java @@ -0,0 +1,40 @@ +package com.rzdata.common.filter; + +import com.rzdata.common.utils.StringUtils; +import org.springframework.http.MediaType; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * Repeatable 过滤器 + * + * @author rzdata + */ +public class RepeatableFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) { + chain.doFilter(request, response); + } else { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() { + + } +} diff --git a/service/src/main/java/com/rzdata/common/filter/RepeatedlyRequestWrapper.java b/service/src/main/java/com/rzdata/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 0000000..1ad3028 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,67 @@ +package com.rzdata.common.filter; + +import cn.hutool.core.io.IoUtil; +import com.rzdata.common.constant.Constants; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * 构建可重复读取inputStream的request + * + * @author rzdata + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException { + super(request); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = IoUtil.readBytes(request.getInputStream(), false); + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public int read() throws IOException { + return bais.read(); + } + + @Override + public int available() throws IOException { + return body.length; + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + }; + } +} diff --git a/service/src/main/java/com/rzdata/common/filter/XssFilter.java b/service/src/main/java/com/rzdata/common/filter/XssFilter.java new file mode 100644 index 0000000..cdad679 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/filter/XssFilter.java @@ -0,0 +1,62 @@ +package com.rzdata.common.filter; + +import com.rzdata.common.enums.HttpMethod; +import com.rzdata.common.utils.StringUtils; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * 防止XSS攻击的过滤器 + * + * @author rzdata + */ +public class XssFilter implements Filter { + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() { + + } +} diff --git a/service/src/main/java/com/rzdata/common/filter/XssHttpServletRequestWrapper.java b/service/src/main/java/com/rzdata/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..f1b058e --- /dev/null +++ b/service/src/main/java/com/rzdata/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,97 @@ +package com.rzdata.common.filter; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HtmlUtil; +import com.rzdata.common.utils.StringUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * XSS过滤处理 + * + * @author rzdata + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + } + + @Override + public String[] getParameterValues(String name) { + String[] values = super.getParameterValues(name); + if (values != null) { + int length = values.length; + String[] escapesValues = new String[length]; + for (int i = 0; i < length; i++) { + // 防xss攻击和过滤前后空格 + escapesValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim(); + } + return escapesValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + // 非json类型,直接返回 + if (!isJsonRequest()) { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8); + if (StringUtils.isEmpty(json)) { + return super.getInputStream(); + } + + // xss过滤 + json = HtmlUtil.cleanHtmlTag(json).trim(); + byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8); + final ByteArrayInputStream bis = IoUtil.toStream(jsonBytes); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public int available() throws IOException { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + + @Override + public int read() throws IOException { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + */ + public boolean isJsonRequest() { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} diff --git a/service/src/main/java/com/rzdata/common/helper/DataBaseHelper.java b/service/src/main/java/com/rzdata/common/helper/DataBaseHelper.java new file mode 100644 index 0000000..d246242 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/helper/DataBaseHelper.java @@ -0,0 +1,72 @@ +package com.rzdata.common.helper; + +import cn.hutool.core.convert.Convert; +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.rzdata.common.enums.DataBaseType; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +/** + * 数据库助手 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DataBaseHelper { + + private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class); + + /** + * 获取当前数据库类型 + */ + public static DataBaseType getDataBaseType() { + DataSource dataSource = DS.determineDataSource(); + try (Connection conn = dataSource.getConnection()) { + DatabaseMetaData metaData = conn.getMetaData(); + String databaseProductName = metaData.getDatabaseProductName(); + return DataBaseType.find(databaseProductName); + } catch (SQLException e) { + throw new ServiceException(e.getMessage()); + } + } + + public static boolean isMySql() { + return DataBaseType.MY_SQL == getDataBaseType(); + } + + public static boolean isOracle() { + return DataBaseType.ORACLE == getDataBaseType(); + } + + public static boolean isPostgerSql() { + return DataBaseType.POSTGRE_SQL == getDataBaseType(); + } + + public static boolean isSqlServer() { + return DataBaseType.SQL_SERVER == getDataBaseType(); + } + + public static String findInSet(Object var1, String var2) { + DataBaseType dataBasyType = getDataBaseType(); + String var = Convert.toStr(var1); + if (dataBasyType == DataBaseType.SQL_SERVER) { + // charindex(',100,' , ',0,100,101,') <> 0 + return "charindex('," + var + ",' , ','+" + var2 + "+',') <> 0"; + } else if (dataBasyType == DataBaseType.POSTGRE_SQL) { + // (select position(',100,' in ',0,100,101,')) <> 0 + return "(select position('," + var + ",' in ','||" + var2 + "||',')) <> 0"; + } else if (dataBasyType == DataBaseType.ORACLE) { + // instr(',0,100,101,' , ',100,') <> 0 + return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0"; + } + // find_in_set(100 , '0,100,101') + return "find_in_set(" + var + " , " + var2 + ") <> 0"; + } +} diff --git a/service/src/main/java/com/rzdata/common/helper/DataPermissionHelper.java b/service/src/main/java/com/rzdata/common/helper/DataPermissionHelper.java new file mode 100644 index 0000000..43588db --- /dev/null +++ b/service/src/main/java/com/rzdata/common/helper/DataPermissionHelper.java @@ -0,0 +1,47 @@ +package com.rzdata.common.helper; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaStorage; +import cn.hutool.core.util.ObjectUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.Map; + +/** + * 数据权限助手 + * + * @author Lion Li + * @version 3.5.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings("unchecked cast") +public class DataPermissionHelper { + + private static final String DATA_PERMISSION_KEY = "data:permission"; + + public static T getVariable(String key) { + Map context = getContext(); + return (T) context.get(key); + } + + + public static void setVariable(String key, Object value) { + Map context = getContext(); + context.put(key, value); + } + + public static Map getContext() { + SaStorage saStorage = SaHolder.getStorage(); + Object attribute = saStorage.get(DATA_PERMISSION_KEY); + if (ObjectUtil.isNull(attribute)) { + saStorage.set(DATA_PERMISSION_KEY, new HashMap<>()); + attribute = saStorage.get(DATA_PERMISSION_KEY); + } + if (attribute instanceof Map) { + return (Map) attribute; + } + throw new NullPointerException("data permission context type exception"); + } +} diff --git a/service/src/main/java/com/rzdata/common/helper/LoginHelper.java b/service/src/main/java/com/rzdata/common/helper/LoginHelper.java new file mode 100644 index 0000000..ccebac5 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/helper/LoginHelper.java @@ -0,0 +1,135 @@ +package com.rzdata.common.helper; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.ObjectUtil; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.enums.DeviceType; +import com.rzdata.common.enums.UserType; +import com.rzdata.common.exception.UtilException; +import com.rzdata.common.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * 登录鉴权助手 + * + * user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app + * deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios + * 可以组成 用户类型与设备类型多对多的 权限灵活控制 + * + * 多用户体系 针对 多种用户类型 但权限控制不一致 + * 可以组成 多用户类型表与多设备类型 分别控制权限 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class LoginHelper { + + public static final String JOIN_CODE = ":"; + public static final String LOGIN_USER_KEY = "loginUser"; + + /** + * 登录系统 + * + * @param loginUser 登录用户信息 + */ + public static void login(LoginUser loginUser) { + SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); + StpUtil.login(loginUser.getLoginId()); + setLoginUser(loginUser); + } + + /** + * 登录系统 基于 设备类型 + * 针对相同用户体系不同设备 + * + * @param loginUser 登录用户信息 + */ + public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) { + SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); + StpUtil.login(loginUser.getLoginId(), deviceType.getDevice()); + setLoginUser(loginUser); + } + + /** + * 设置用户数据(多级缓存) + */ + public static void setLoginUser(LoginUser loginUser) { + StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser); + } + + /** + * 获取用户(多级缓存) + */ + public static LoginUser getLoginUser() { + LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY); + if (loginUser != null) { + return loginUser; + } + loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY); + SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); + return loginUser; + } + + /** + * 获取用户id + */ + public static Long getUserId() { + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNull(loginUser)) { + String loginId = StpUtil.getLoginIdAsString(); + String userId = null; + for (UserType value : UserType.values()) { + if (StringUtils.contains(loginId, value.getUserType())) { + String[] strs = StringUtils.split(loginId, JOIN_CODE); + // 用户id在总是在最后 + userId = strs[strs.length - 1]; + } + } + if (StringUtils.isBlank(userId)) { + throw new UtilException("登录用户: LoginId异常 => " + loginId); + } + return Long.parseLong(userId); + } + return loginUser.getUserId(); + } + + /** + * 获取部门ID + */ + public static Long getDeptId() { + return getLoginUser().getDeptId(); + } + + /** + * 获取用户账户 + */ + public static String getUsername() { + return getLoginUser().getUsername(); + } + + /** + * 获取用户类型 + */ + public static UserType getUserType() { + String loginId = StpUtil.getLoginIdAsString(); + return UserType.getUserType(loginId); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) { + return UserConstants.ADMIN_ID.equals(userId); + } + + public static boolean isAdmin() { + return isAdmin(getUserId()); + } + +} diff --git a/service/src/main/java/com/rzdata/common/jackson/DictDataJsonSerializer.java b/service/src/main/java/com/rzdata/common/jackson/DictDataJsonSerializer.java new file mode 100644 index 0000000..d0369a3 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/jackson/DictDataJsonSerializer.java @@ -0,0 +1,56 @@ +package com.rzdata.common.jackson; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.rzdata.common.annotation.DictDataMapper; +import com.rzdata.common.core.service.DictService; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; + +import java.io.IOException; +import java.util.Objects; + +/** + * 字典数据json序列化工具 + * + * @author itino + */ +@Slf4j +public class DictDataJsonSerializer extends JsonSerializer implements ContextualSerializer { + + private String dictType; + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + try { + DictService dictService = SpringUtils.getBean(DictService.class); + if (ObjectUtil.isNotNull(dictService)) { + String label = dictService.getDictLabel(dictType, value); + gen.writeString(StringUtils.isNotBlank(label) ? label : value); + } else { + gen.writeString(value); + } + } catch (BeansException e) { + log.error("字典数据未查到, 采用默认处理 => {}", e.getMessage()); + gen.writeString(value); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { + DictDataMapper anno = property.getAnnotation(DictDataMapper.class); + if (Objects.nonNull(anno) && StrUtil.isNotBlank(anno.dictType())) { + this.dictType = anno.dictType(); + return this; + } + return prov.findValueSerializer(property.getType(), property); + } +} diff --git a/service/src/main/java/com/rzdata/common/jackson/SensitiveJsonSerializer.java b/service/src/main/java/com/rzdata/common/jackson/SensitiveJsonSerializer.java new file mode 100644 index 0000000..f2fd27a --- /dev/null +++ b/service/src/main/java/com/rzdata/common/jackson/SensitiveJsonSerializer.java @@ -0,0 +1,54 @@ +package com.rzdata.common.jackson; + +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.rzdata.common.annotation.Sensitive; +import com.rzdata.common.core.service.SensitiveService; +import com.rzdata.common.enums.SensitiveStrategy; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; + +import java.io.IOException; +import java.util.Objects; + +/** + * 数据脱敏json序列化工具 + * + * @author Yjoioooo + */ +@Slf4j +public class SensitiveJsonSerializer extends JsonSerializer implements ContextualSerializer { + + private SensitiveStrategy strategy; + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + try { + SensitiveService sensitiveService = SpringUtils.getBean(SensitiveService.class); + if (ObjectUtil.isNotNull(sensitiveService) && sensitiveService.isSensitive()) { + gen.writeString(strategy.desensitizer().apply(value)); + } else { + gen.writeString(value); + } + } catch (BeansException e) { + log.error("脱敏实现不存在, 采用默认处理 => {}", e.getMessage()); + gen.writeString(value); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { + Sensitive annotation = property.getAnnotation(Sensitive.class); + if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) { + this.strategy = annotation.strategy(); + return this; + } + return prov.findValueSerializer(property.getType(), property); + } +} diff --git a/service/src/main/java/com/rzdata/common/utils/BeanCopyUtils.java b/service/src/main/java/com/rzdata/common/utils/BeanCopyUtils.java new file mode 100644 index 0000000..6442778 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/BeanCopyUtils.java @@ -0,0 +1,184 @@ +package com.rzdata.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.cglib.beans.BeanCopier; +import org.springframework.cglib.beans.BeanMap; +import org.springframework.cglib.core.Converter; + +import java.util.List; +import java.util.Map; + +/** + * bean深拷贝工具(基于 cglib 性能优异) + *

+ * 重点 cglib 不支持 拷贝到链式对象 + * 例如: 源对象 拷贝到 目标(链式对象) + * 请区分好`浅拷贝`和`深拷贝`再做使用 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class BeanCopyUtils { + + /** + * 单对象基于class创建拷贝 + * + * @param source 数据来源实体 + * @param desc 描述对象 转换后的对象 + * @return desc + */ + public static V copy(T source, Class desc) { + if (ObjectUtil.isNull(source)) { + return null; + } + if (ObjectUtil.isNull(desc)) { + return null; + } + final V target = ReflectUtil.newInstanceIfPossible(desc); + return copy(source, target); + } + + /** + * 单对象基于对象创建拷贝 + * + * @param source 数据来源实体 + * @param desc 转换后的对象 + * @return desc + */ + public static V copy(T source, V desc) { + if (ObjectUtil.isNull(source)) { + return null; + } + if (ObjectUtil.isNull(desc)) { + return null; + } + BeanCopier beanCopier = BeanCopierCache.INSTANCE.get(source.getClass(), desc.getClass(), null); + beanCopier.copy(source, desc, null); + return desc; + } + + /** + * 列表对象基于class创建拷贝 + * + * @param sourceList 数据来源实体列表 + * @param desc 描述对象 转换后的对象 + * @return desc + */ + public static List copyList(List sourceList, Class desc) { + if (ObjectUtil.isNull(sourceList)) { + return null; + } + if (CollUtil.isEmpty(sourceList)) { + return CollUtil.newArrayList(); + } + return StreamUtils.toList(sourceList, source -> { + V target = ReflectUtil.newInstanceIfPossible(desc); + copy(source, target); + return target; + }); + } + + /** + * bean拷贝到map + * + * @param bean 数据来源实体 + * @return map对象 + */ + @SuppressWarnings("unchecked") + public static Map copyToMap(T bean) { + if (ObjectUtil.isNull(bean)) { + return null; + } + return BeanMap.create(bean); + } + + /** + * map拷贝到bean + * + * @param map 数据来源 + * @param beanClass bean类 + * @return bean对象 + */ + public static T mapToBean(Map map, Class beanClass) { + if (MapUtil.isEmpty(map)) { + return null; + } + if (ObjectUtil.isNull(beanClass)) { + return null; + } + T bean = ReflectUtil.newInstanceIfPossible(beanClass); + return mapToBean(map, bean); + } + + /** + * map拷贝到bean + * + * @param map 数据来源 + * @param bean bean对象 + * @return bean对象 + */ + public static T mapToBean(Map map, T bean) { + if (MapUtil.isEmpty(map)) { + return null; + } + if (ObjectUtil.isNull(bean)) { + return null; + } + BeanMap.create(bean).putAll(map); + return bean; + } + + /** + * BeanCopier属性缓存
+ * 缓存用于防止多次反射造成的性能问题 + * + * @author Looly + * @since 5.4.1 + */ + public enum BeanCopierCache { + /** + * BeanCopier属性缓存单例 + */ + INSTANCE; + + private final SimpleCache cache = new SimpleCache<>(); + + /** + * 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素 + * + * @param srcClass 源Bean的类 + * @param targetClass 目标Bean的类 + * @param converter 转换器 + * @return Map中对应的BeanCopier + */ + public BeanCopier get(Class srcClass, Class targetClass, Converter converter) { + final String key = genKey(srcClass, targetClass, converter); + return cache.get(key, () -> BeanCopier.create(srcClass, targetClass, converter != null)); + } + + /** + * 获得类与转换器生成的key + * + * @param srcClass 源Bean的类 + * @param targetClass 目标Bean的类 + * @param converter 转换器 + * @return 属性名和Map映射的key + */ + private String genKey(Class srcClass, Class targetClass, Converter converter) { + final StringBuilder key = StrUtil.builder() + .append(srcClass.getName()).append('#').append(targetClass.getName()); + if(null != converter){ + key.append('#').append(converter.getClass().getName()); + } + return key.toString(); + } + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/DateUtils.java b/service/src/main/java/com/rzdata/common/utils/DateUtils.java new file mode 100644 index 0000000..06420ad --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/DateUtils.java @@ -0,0 +1,168 @@ +package com.rzdata.common.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; + +/** + * 时间工具类 + * + * @author rzdata + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + + public static final String YYYY = "yyyy"; + + public static final String YYYY_MM = "yyyy-MM"; + + public static final String YYYY_MM_DD = "yyyy-MM-dd"; + + public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static final String[] PARSE_PATTERNS = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), PARSE_PATTERNS); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/service/src/main/java/com/rzdata/common/utils/JsonUtils.java b/service/src/main/java/com/rzdata/common/utils/JsonUtils.java new file mode 100644 index 0000000..cb76bc0 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/JsonUtils.java @@ -0,0 +1,112 @@ +package com.rzdata.common.utils; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * JSON 工具类 + * + * @author 芋道源码 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class JsonUtils { + + private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class); + + public static ObjectMapper getObjectMapper() { + return OBJECT_MAPPER; + } + + public static String toJsonString(Object object) { + if (ObjectUtil.isNull(object)) { + return null; + } + try { + return OBJECT_MAPPER.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, Class clazz) { + if (StringUtils.isEmpty(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(bytes, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, TypeReference typeReference) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Dict parseMap(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class)); + } catch (MismatchedInputException e) { + // 类型不匹配说明不是json + return null; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List parseArrayMap(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List parseArray(String text, Class clazz) { + if (StringUtils.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/MessageUtils.java b/service/src/main/java/com/rzdata/common/utils/MessageUtils.java new file mode 100644 index 0000000..03cc8e0 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/MessageUtils.java @@ -0,0 +1,29 @@ +package com.rzdata.common.utils; + +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * 获取i18n资源文件 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MessageUtils { + + private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class); + + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) { + return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/service/src/main/java/com/rzdata/common/utils/ServletUtils.java b/service/src/main/java/com/rzdata/common/utils/ServletUtils.java new file mode 100644 index 0000000..b256fca --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/ServletUtils.java @@ -0,0 +1,203 @@ +package com.rzdata.common.utils; + +import cn.hutool.core.convert.Convert; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.http.HttpStatus; +import com.rzdata.common.constant.Constants; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * 客户端工具类 + * + * @author rzdata + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ServletUtils extends ServletUtil { + + /** + * 获取String参数 + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParams(ServletRequest request) { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParamMap(ServletRequest request) { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) { + try { + response.setStatus(HttpStatus.HTTP_OK); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) { + + String accept = request.getHeader("accept"); + if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml"); + } + + public static String getClientIP() { + return getClientIP(getRequest()); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) { + try { + return URLEncoder.encode(str, Constants.UTF8); + } catch (UnsupportedEncodingException e) { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) { + try { + return URLDecoder.decode(str, Constants.UTF8); + } catch (UnsupportedEncodingException e) { + return StringUtils.EMPTY; + } + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/StreamUtils.java b/service/src/main/java/com/rzdata/common/utils/StreamUtils.java new file mode 100644 index 0000000..9ca71e2 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/StreamUtils.java @@ -0,0 +1,251 @@ +package com.rzdata.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * stream 流工具类 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class StreamUtils { + + /** + * 将collection过滤 + * + * @param collection 需要转化的集合 + * @param function 过滤方法 + * @return 过滤后的list + */ + public static List filter(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + return collection.stream().filter(function).collect(Collectors.toList()); + } + + /** + * 将collection拼接 + * + * @param collection 需要转化的集合 + * @param function 拼接方法 + * @return 拼接后的list + */ + public static String join(Collection collection, Function function) { + return join(collection, function, ","); + } + + /** + * 将collection拼接 + * + * @param collection 需要转化的集合 + * @param function 拼接方法 + * @param delimiter 拼接符 + * @return 拼接后的list + */ + public static String join(Collection collection, Function function, CharSequence delimiter) { + if (CollUtil.isEmpty(collection)) { + return StringUtils.EMPTY; + } + return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter)); + } + + /** + * 将collection排序 + * + * @param collection 需要转化的集合 + * @param comparing 排序方法 + * @return 排序后的list + */ + public static List sorted(Collection collection, Comparator comparing) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + return collection.stream().sorted(comparing).collect(Collectors.toList()); + } + + /** + * 将collection转化为类型不变的map
+ * {@code Collection ----> Map} + * + * @param collection 需要转化的集合 + * @param key V类型转化为K类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @return 转化后的map + */ + public static Map toIdentityMap(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + } + + /** + * 将Collection转化为map(value类型与collection的泛型不同)
+ * {@code Collection -----> Map } + * + * @param collection 需要转化的集合 + * @param key E类型转化为K类型的lambda方法 + * @param value E类型转化为V类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @param map中的value类型 + * @return 转化后的map + */ + public static Map toMap(Collection collection, Function key, Function value) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l)); + } + + /** + * 将collection按照规则(比如有相同的班级id)分类成map
+ * {@code Collection -------> Map> } + * + * @param collection 需要分类的集合 + * @param key 分类的规则 + * @param collection中的泛型 + * @param map中的key类型 + * @return 分类后的map + */ + public static Map> groupByKey(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection + .stream() + .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList())); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map>> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 集合元素类型 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @return 分类后的map + */ + public static Map>> groupBy2Key(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection + .stream() + .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList()))); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @param collection中的泛型 + * @return 分类后的map + */ + public static Map> group2Map(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { + return MapUtil.newHashMap(); + } + return collection + .stream() + .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); + } + + /** + * 将collection转化为List集合,但是两者的泛型不同
+ * {@code Collection ------> List } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为list泛型的lambda表达式 + * @param collection中的泛型 + * @param List中的泛型 + * @return 转化后的list + */ + public static List toList(Collection collection, Function function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + return collection + .stream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + /** + * 将collection转化为Set集合,但是两者的泛型不同
+ * {@code Collection ------> Set } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为set泛型的lambda表达式 + * @param collection中的泛型 + * @param Set中的泛型 + * @return 转化后的Set + */ + public static Set toSet(Collection collection, Function function) { + if (CollUtil.isEmpty(collection) || function == null) { + return CollUtil.newHashSet(); + } + return collection + .stream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + + /** + * 合并两个相同key类型的map + * + * @param map1 第一个需要合并的 map + * @param map2 第二个需要合并的 map + * @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况 + * @param map中的key类型 + * @param 第一个 map的value类型 + * @param 第二个 map的value类型 + * @param 最终map的value类型 + * @return 合并后的map + */ + public static Map merge(Map map1, Map map2, BiFunction merge) { + if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) { + return MapUtil.newHashMap(); + } else if (MapUtil.isEmpty(map1)) { + map1 = MapUtil.newHashMap(); + } else if (MapUtil.isEmpty(map2)) { + map2 = MapUtil.newHashMap(); + } + Set key = new HashSet<>(); + key.addAll(map1.keySet()); + key.addAll(map2.keySet()); + Map map = new HashMap<>(); + for (K t : key) { + X x = map1.get(t); + Y y = map2.get(t); + V z = merge.apply(x, y); + if (z != null) { + map.put(t, z); + } + } + return map; + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/StringUtils.java b/service/src/main/java/com/rzdata/common/utils/StringUtils.java new file mode 100644 index 0000000..3220025 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/StringUtils.java @@ -0,0 +1,273 @@ +package com.rzdata.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.util.AntPathMatcher; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 字符串工具类 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class StringUtils extends org.apache.commons.lang3.StringUtils { + + /** + * 获取参数不为空值 + * + * @param str defaultValue 要判断的value + * @return value 返回值 + */ + public static String blankToDefault(String str, String defaultValue) { + return StrUtil.blankToDefault(str, defaultValue); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) { + return StrUtil.isEmpty(str); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + /** + * 去空格 + */ + public static String trim(String str) { + return StrUtil.trim(str); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) { + return substring(str, start, str.length()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) { + return StrUtil.sub(str, start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is {} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) { + return StrUtil.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) { + return Validator.isUrl(link); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static Set str2Set(String str, String sep) { + return new HashSet<>(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static List str2List(String str, String sep, boolean filterBlank, boolean trim) { + List list = new ArrayList<>(); + if (isEmpty(str)) { + return list; + } + + // 过滤空白字符串 + if (filterBlank && isBlank(str)) { + return list; + } + String[] split = str.split(sep); + for (String string : split) { + if (filterBlank && isBlank(string)) { + continue; + } + if (trim) { + string = trim(string); + } + list.add(string); + } + + return list; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) { + return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences); + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) { + return StrUtil.toUnderlineCase(str); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) { + return StrUtil.equalsAnyIgnoreCase(str, strs); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) { + return StrUtil.upperFirst(StrUtil.toCamelCase(name)); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) { + return StrUtil.toCamelCase(s); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) { + if (isEmpty(str) || CollUtil.isEmpty(strs)) { + return false; + } + for (String pattern : strs) { + if (isMatch(pattern, str)) { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) { + final StringBuilder sb = new StringBuilder(size); + if (s != null) { + final int len = s.length(); + if (s.length() <= size) { + for (int i = size - len; i > 0; i--) { + sb.append(c); + } + sb.append(s); + } else { + return s.substring(len - size, len); + } + } else { + for (int i = size; i > 0; i--) { + sb.append(c); + } + } + return sb.toString(); + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/Threads.java b/service/src/main/java/com/rzdata/common/utils/Threads.java new file mode 100644 index 0000000..af1523f --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/Threads.java @@ -0,0 +1,75 @@ +package com.rzdata.common.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.*; + +/** + * 线程相关工具类. + * + * @author rzdata + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Threads { + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) { + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + log.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + log.error(t.getMessage(), t); + } + } +} diff --git a/service/src/main/java/com/rzdata/common/utils/TreeBuildUtils.java b/service/src/main/java/com/rzdata/common/utils/TreeBuildUtils.java new file mode 100644 index 0000000..f6c7eef --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/TreeBuildUtils.java @@ -0,0 +1,35 @@ +package com.rzdata.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.lang.tree.TreeUtil; +import cn.hutool.core.lang.tree.parser.NodeParser; +import com.rzdata.common.utils.reflect.ReflectUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 扩展 hutool TreeUtil 封装系统树构建 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TreeBuildUtils extends TreeUtil { + + /** + * 根据前端定制差异化字段 + */ + public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label"); + + public static List> build(List list, NodeParser nodeParser) { + if (CollUtil.isEmpty(list)) { + return null; + } + K k = ReflectUtils.invokeGetter(list.get(0), "parentId"); + return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser); + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/ValidatorUtils.java b/service/src/main/java/com/rzdata/common/utils/ValidatorUtils.java new file mode 100644 index 0000000..50d4397 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/ValidatorUtils.java @@ -0,0 +1,29 @@ +package com.rzdata.common.utils; + +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; +import java.util.Set; + +/** + * Validator 校验框架工具 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ValidatorUtils { + + private static final Validator VALID = SpringUtils.getBean(Validator.class); + + public static void validate(T object, Class... groups) { + Set> validate = VALID.validate(object, groups); + if (!validate.isEmpty()) { + throw new ConstraintViolationException("参数校验异常", validate); + } + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/email/MailUtils.java b/service/src/main/java/com/rzdata/common/utils/email/MailUtils.java new file mode 100644 index 0000000..571226c --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/email/MailUtils.java @@ -0,0 +1,468 @@ +package com.rzdata.common.utils.email; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.mail.*; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import javax.mail.Authenticator; +import javax.mail.Session; +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; + + +/** + * 邮件工具类 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MailUtils { + + private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); + + /** + * 获取邮件发送实例 + */ + public static MailAccount getMailAccount() { + return ACCOUNT; + } + + /** + * 获取邮件发送实例 (自定义发送人以及授权码) + * + * @param user 发送人 + * @param pass 授权码 + */ + public static MailAccount getMailAccount(String from, String user, String pass) { + ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom())); + ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser())); + ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass())); + return ACCOUNT; + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendText(String to, String subject, String content, File... files) { + return send(to, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, File... files) { + return send(to, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + */ + public static String sendText(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, Map imageMap, File... files) { + return send(to, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, Map imageMap, File... files) { + return send(tos, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, + boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + /** + * 根据配置文件,获取邮件客户端会话 + * + * @param mailAccount 邮件账户配置 + * @param isSingleton 是否单例(全局共享会话) + * @return {@link Session} + * @since 5.5.7 + */ + public static Session getSession(MailAccount mailAccount, boolean isSingleton) { + Authenticator authenticator = null; + if (mailAccount.isAuth()) { + authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); + } + + return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) // + : Session.getInstance(mailAccount.getSmtpProps(), authenticator); + } + + // ------------------------------------------------------------------------------------------------------------------------ Private method start + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param useGlobalSession 是否全局共享Session + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:${cid} + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content, + Map imageMap, boolean isHtml, File... files) { + final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession); + + // 可选抄送人 + if (CollUtil.isNotEmpty(ccs)) { + mail.setCcs(ccs.toArray(new String[0])); + } + // 可选密送人 + if (CollUtil.isNotEmpty(bccs)) { + mail.setBccs(bccs.toArray(new String[0])); + } + + mail.setTos(tos.toArray(new String[0])); + mail.setTitle(subject); + mail.setContent(content); + mail.setHtml(isHtml); + mail.setFiles(files); + + // 图片 + if (MapUtil.isNotEmpty(imageMap)) { + for (Map.Entry entry : imageMap.entrySet()) { + mail.addImage(entry.getKey(), entry.getValue()); + // 关闭流 + IoUtil.close(entry.getValue()); + } + } + + return mail.send(); + } + + /** + * 将多个联系人转为列表,分隔符为逗号或者分号 + * + * @param addresses 多个联系人,如果为空返回null + * @return 联系人列表 + */ + private static List splitAddress(String addresses) { + if (StrUtil.isBlank(addresses)) { + return null; + } + + List result; + if (StrUtil.contains(addresses, CharUtil.COMMA)) { + result = StrUtil.splitTrim(addresses, CharUtil.COMMA); + } else if (StrUtil.contains(addresses, ';')) { + result = StrUtil.splitTrim(addresses, ';'); + } else { + result = CollUtil.newArrayList(addresses); + } + return result; + } + // ------------------------------------------------------------------------------------------------------------------------ Private method end + +} diff --git a/service/src/main/java/com/rzdata/common/utils/file/FileUtils.java b/service/src/main/java/com/rzdata/common/utils/file/FileUtils.java new file mode 100644 index 0000000..2c77d01 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/file/FileUtils.java @@ -0,0 +1,52 @@ +package com.rzdata.common.utils.file; + +import cn.hutool.core.io.FileUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class FileUtils extends FileUtil { + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/service/src/main/java/com/rzdata/common/utils/file/MimeTypeUtils.java b/service/src/main/java/com/rzdata/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..16184a3 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,40 @@ +package com.rzdata.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author rzdata + */ +public class MimeTypeUtils { + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; + + public static final String[] FLASH_EXTENSION = {"swf", "flv"}; + + public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb"}; + + public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf"}; + +} diff --git a/service/src/main/java/com/rzdata/common/utils/ip/AddressUtils.java b/service/src/main/java/com/rzdata/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..2dc7dfb --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/ip/AddressUtils.java @@ -0,0 +1,60 @@ +package com.rzdata.common.utils.ip; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.net.NetUtil; +import cn.hutool.http.HtmlUtil; +import cn.hutool.http.HttpUtil; +import com.rzdata.common.config.RuoYiConfig; +import com.rzdata.common.constant.Constants; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 获取地址类 + * + * @author Lion Li + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class AddressUtils { + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) { + String address = UNKNOWN; + if (StringUtils.isBlank(ip)) { + return address; + } + // 内网不查询 + ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); + if (NetUtil.isInnerIP(ip)) { + return "内网IP"; + } + if (RuoYiConfig.isAddressEnabled()) { + try { + String rspStr = HttpUtil.createGet(IP_URL) + .body("ip=" + ip + "&json=true", Constants.GBK) + .execute() + .body(); + if (StringUtils.isEmpty(rspStr)) { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + Dict obj = JsonUtils.parseMap(rspStr); + String region = obj.getStr("pro"); + String city = obj.getStr("city"); + return String.format("%s %s", region, city); + } catch (Exception e) { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/service/src/main/java/com/rzdata/common/utils/poi/ExcelUtil.java b/service/src/main/java/com/rzdata/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..89056dc --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/poi/ExcelUtil.java @@ -0,0 +1,328 @@ +package com.rzdata.common.utils.poi; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.resource.ClassPathResource; +import cn.hutool.core.util.IdUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import com.rzdata.common.convert.ExcelBigNumberConvert; +import com.rzdata.common.excel.CellMergeStrategy; +import com.rzdata.common.excel.DefaultExcelListener; +import com.rzdata.common.excel.ExcelListener; +import com.rzdata.common.excel.ExcelResult; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.file.FileUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Excel相关处理 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExcelUtil { + + /** + * 同步导入(适用于小数据量) + * + * @param is 输入流 + * @return 转换后集合 + */ + public static List importExcel(InputStream is, Class clazz) { + return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); + } + + + /** + * 使用校验监听器 异步导入 同步返回 + * + * @param is 输入流 + * @param clazz 对象类型 + * @param isValidate 是否 Validator 检验 默认为是 + * @return 转换后集合 + */ + public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { + DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); + EasyExcel.read(is, clazz, listener).sheet().doRead(); + return listener.getExcelResult(); + } + + /** + * 使用自定义监听器 异步导入 自定义返回 + * + * @param is 输入流 + * @param clazz 对象类型 + * @param listener 自定义监听器 + * @return 转换后集合 + */ + public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { + EasyExcel.read(is, clazz, listener).sheet().doRead(); + return listener.getExcelResult(); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, false, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, merge, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { + exportExcel(list, sheetName, clazz, false, os); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os) { + ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) + .autoCloseStream(false) + // 自动适配 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .sheet(sheetName); + if (merge) { + // 合并处理器 + builder.registerWriteHandler(new CellMergeStrategy(list, true)); + } + builder.doWrite(list); + } + + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { + try { + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplate(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplate(List data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = EasyExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + // 单表多数据导出 模板格式为 {.属性} + for (Object d : data) { + excelWriter.fill(d, writeSheet); + } + excelWriter.finish(); + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { + try { + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplateMultiList(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = EasyExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + for (Map.Entry map : data.entrySet()) { + // 设置列表后续还有数据 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + // 多表导出必须使用 FillWrapper + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), writeSheet); + } + } + excelWriter.finish(); + } + + /** + * 重置响应体 + */ + private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { + String filename = encodingFilename(sheetName); + FileUtils.setAttachmentResponseHeader(response, filename); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[0].equals(value)) { + propertyString.append(itemArray[1] + separator); + break; + } + } + } else { + if (itemArray[0].equals(propertyValue)) { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[1].equals(value)) { + propertyString.append(itemArray[0] + separator); + break; + } + } + } else { + if (itemArray[1].equals(propertyValue)) { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 编码文件名 + */ + public static String encodingFilename(String filename) { + return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx"; + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/redis/CacheUtils.java b/service/src/main/java/com/rzdata/common/utils/redis/CacheUtils.java new file mode 100644 index 0000000..b62e340 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/redis/CacheUtils.java @@ -0,0 +1,75 @@ +package com.rzdata.common.utils.redis; + +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.redisson.api.RMap; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; + +import java.util.Set; + +/** + * 缓存操作工具类 {@link } + * + * @author Michelle.Chung + * @date 2022/8/13 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings(value = {"unchecked"}) +public class CacheUtils { + + private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class); + + /** + * 获取缓存组内所有的KEY + * + * @param cacheNames 缓存组名称 + */ + public static Set keys(String cacheNames) { + RMap rmap = (RMap) CACHE_MANAGER.getCache(cacheNames).getNativeCache(); + return rmap.keySet(); + } + + /** + * 获取缓存值 + * + * @param cacheNames 缓存组名称 + * @param key 缓存key + */ + public static T get(String cacheNames, Object key) { + Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key); + return wrapper != null ? (T) wrapper.get() : null; + } + + /** + * 保存缓存值 + * + * @param cacheNames 缓存组名称 + * @param key 缓存key + * @param value 缓存值 + */ + public static void put(String cacheNames, Object key, Object value) { + CACHE_MANAGER.getCache(cacheNames).put(key, value); + } + + /** + * 删除缓存值 + * + * @param cacheNames 缓存组名称 + * @param key 缓存key + */ + public static void evict(String cacheNames, Object key) { + CACHE_MANAGER.getCache(cacheNames).evict(key); + } + + /** + * 清空缓存值 + * + * @param cacheNames 缓存组名称 + */ + public static void clear(String cacheNames) { + CACHE_MANAGER.getCache(cacheNames).clear(); + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/redis/QueueUtils.java b/service/src/main/java/com/rzdata/common/utils/redis/QueueUtils.java new file mode 100644 index 0000000..8afba35 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/redis/QueueUtils.java @@ -0,0 +1,180 @@ +package com.rzdata.common.utils.redis; + +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.redisson.api.*; + +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * 分布式队列工具 + * 轻量级队列 重量级数据量 请使用 MQ + * 要求 redis 5.X 以上 + * + * @author Lion Li + * @version 3.6.0 新增 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class QueueUtils { + + private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); + + + /** + * 获取客户端实例 + */ + public static RedissonClient getClient() { + return CLIENT; + } + + /** + * 添加普通队列数据 + * + * @param queueName 队列名 + * @param data 数据 + */ + public static boolean addQueueObject(String queueName, T data) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + return queue.offer(data); + } + + /** + * 通用获取一个队列数据 没有数据返回 null(不支持延迟队列) + * + * @param queueName 队列名 + */ + public static T getQueueObject(String queueName) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + return queue.poll(); + } + + /** + * 通用删除队列数据(不支持延迟队列) + */ + public static boolean removeQueueObject(String queueName, T data) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + return queue.remove(data); + } + + /** + * 通用销毁队列 所有阻塞监听 报错(不支持延迟队列) + */ + public static boolean destroyQueue(String queueName) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + return queue.delete(); + } + + /** + * 添加延迟队列数据 默认毫秒 + * + * @param queueName 队列名 + * @param data 数据 + * @param time 延迟时间 + */ + public static void addDelayedQueueObject(String queueName, T data, long time) { + addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS); + } + + /** + * 添加延迟队列数据 + * + * @param queueName 队列名 + * @param data 数据 + * @param time 延迟时间 + * @param timeUnit 单位 + */ + public static void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); + delayedQueue.offer(data, time, timeUnit); + } + + /** + * 获取一个延迟队列数据 没有数据返回 null + * + * @param queueName 队列名 + */ + public static T getDelayedQueueObject(String queueName) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); + return delayedQueue.poll(); + } + + /** + * 删除延迟队列数据 + */ + public static boolean removeDelayedQueueObject(String queueName, T data) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); + return delayedQueue.remove(data); + } + + /** + * 销毁延迟队列 所有阻塞监听 报错 + */ + public static void destroyDelayedQueue(String queueName) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); + delayedQueue.destroy(); + } + + /** + * 添加优先队列数据 + * + * @param queueName 队列名 + * @param data 数据 + */ + public static boolean addPriorityQueueObject(String queueName, T data) { + RPriorityBlockingQueue priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName); + return priorityBlockingQueue.offer(data); + } + + /** + * 尝试设置 有界队列 容量 用于限制数量 + * + * @param queueName 队列名 + * @param capacity 容量 + */ + public static boolean trySetBoundedQueueCapacity(String queueName, int capacity) { + RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); + return boundedBlockingQueue.trySetCapacity(capacity); + } + + /** + * 尝试设置 有界队列 容量 用于限制数量 + * + * @param queueName 队列名 + * @param capacity 容量 + * @param destroy 已存在是否销毁 + */ + public static boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) { + RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); + if (boundedBlockingQueue.isExists() && destroy) { + destroyQueue(queueName); + } + return boundedBlockingQueue.trySetCapacity(capacity); + } + + /** + * 添加有界队列数据 + * + * @param queueName 队列名 + * @param data 数据 + * @return 添加成功 true 已达到界限 false + */ + public static boolean addBoundedQueueObject(String queueName, T data) { + RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); + return boundedBlockingQueue.offer(data); + } + + /** + * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等) + */ + public static void subscribeBlockingQueue(String queueName, Consumer consumer) { + RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); + queue.subscribeOnElements(consumer); + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/redis/RedisUtils.java b/service/src/main/java/com/rzdata/common/utils/redis/RedisUtils.java new file mode 100644 index 0000000..b846af6 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/redis/RedisUtils.java @@ -0,0 +1,462 @@ +package com.rzdata.common.utils.redis; + +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.redisson.api.*; + +import java.time.Duration; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * redis 工具类 + * + * @author Lion Li + * @version 3.1.0 新增 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings(value = {"unchecked", "rawtypes"}) +public class RedisUtils { + + private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); + + /** + * 限流 + * + * @param key 限流key + * @param rateType 限流类型 + * @param rate 速率 + * @param rateInterval 速率间隔 + * @return -1 表示失败 + */ + public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) { + RRateLimiter rateLimiter = CLIENT.getRateLimiter(key); + rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS); + if (rateLimiter.tryAcquire()) { + return rateLimiter.availablePermits(); + } else { + return -1L; + } + } + + /** + * 获取客户端实例 + */ + public static RedissonClient getClient() { + return CLIENT; + } + + /** + * 发布通道消息 + * + * @param channelKey 通道key + * @param msg 发送数据 + * @param consumer 自定义处理 + */ + public static void publish(String channelKey, T msg, Consumer consumer) { + RTopic topic = CLIENT.getTopic(channelKey); + topic.publish(msg); + consumer.accept(msg); + } + + public static void publish(String channelKey, T msg) { + RTopic topic = CLIENT.getTopic(channelKey); + topic.publish(msg); + } + + /** + * 订阅通道接收消息 + * + * @param channelKey 通道key + * @param clazz 消息类型 + * @param consumer 自定义处理 + */ + public static void subscribe(String channelKey, Class clazz, Consumer consumer) { + RTopic topic = CLIENT.getTopic(channelKey); + topic.addListener(clazz, (channel, msg) -> consumer.accept(msg)); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public static void setCacheObject(final String key, final T value) { + setCacheObject(key, value, false); + } + + /** + * 缓存基本的对象,保留当前对象 TTL 有效期 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param isSaveTtl 是否保留TTL有效期(例如: set之前ttl剩余90 set之后还是为90) + * @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案 + */ + public static void setCacheObject(final String key, final T value, final boolean isSaveTtl) { + RBucket bucket = CLIENT.getBucket(key); + if (isSaveTtl) { + try { + bucket.setAndKeepTTL(value); + } catch (Exception e) { + long timeToLive = bucket.remainTimeToLive(); + setCacheObject(key, value, Duration.ofMillis(timeToLive)); + } + } else { + bucket.set(value); + } + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param duration 时间 + */ + public static void setCacheObject(final String key, final T value, final Duration duration) { + RBatch batch = CLIENT.createBatch(); + RBucketAsync bucket = batch.getBucket(key); + bucket.setAsync(value); + bucket.expireAsync(duration); + batch.execute(); + } + + /** + * 注册对象监听器 + *

+ * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 + * + * @param key 缓存的键值 + * @param listener 监听器配置 + */ + public static void addObjectListener(final String key, final ObjectListener listener) { + RBucket result = CLIENT.getBucket(key); + result.addListener(listener); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public static boolean expire(final String key, final long timeout) { + return expire(key, Duration.ofSeconds(timeout)); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param duration 超时时间 + * @return true=设置成功;false=设置失败 + */ + public static boolean expire(final String key, final Duration duration) { + RBucket rBucket = CLIENT.getBucket(key); + return rBucket.expire(duration); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public static T getCacheObject(final String key) { + RBucket rBucket = CLIENT.getBucket(key); + return rBucket.get(); + } + + /** + * 获得key剩余存活时间 + * + * @param key 缓存键值 + * @return 剩余存活时间 + */ + public static long getTimeToLive(final String key) { + RBucket rBucket = CLIENT.getBucket(key); + return rBucket.remainTimeToLive(); + } + + /** + * 删除单个对象 + * + * @param key 缓存的键值 + */ + public static boolean deleteObject(final String key) { + return CLIENT.getBucket(key).delete(); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + */ + public static void deleteObject(final Collection collection) { + RBatch batch = CLIENT.createBatch(); + collection.forEach(t -> { + batch.getBucket(t.toString()).deleteAsync(); + }); + batch.execute(); + } + + /** + * 检查缓存对象是否存在 + * + * @param key 缓存的键值 + */ + public static boolean isExistsObject(final String key) { + return CLIENT.getBucket(key).isExists(); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public static boolean setCacheList(final String key, final List dataList) { + RList rList = CLIENT.getList(key); + return rList.addAll(dataList); + } + + /** + * 注册List监听器 + *

+ * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 + * + * @param key 缓存的键值 + * @param listener 监听器配置 + */ + public static void addListListener(final String key, final ObjectListener listener) { + RList rList = CLIENT.getList(key); + rList.addListener(listener); + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public static List getCacheList(final String key) { + RList rList = CLIENT.getList(key); + return rList.readAll(); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public static boolean setCacheSet(final String key, final Set dataSet) { + RSet rSet = CLIENT.getSet(key); + return rSet.addAll(dataSet); + } + + /** + * 注册Set监听器 + *

+ * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 + * + * @param key 缓存的键值 + * @param listener 监听器配置 + */ + public static void addSetListener(final String key, final ObjectListener listener) { + RSet rSet = CLIENT.getSet(key); + rSet.addListener(listener); + } + + /** + * 获得缓存的set + * + * @param key 缓存的key + * @return set对象 + */ + public static Set getCacheSet(final String key) { + RSet rSet = CLIENT.getSet(key); + return rSet.readAll(); + } + + /** + * 缓存Map + * + * @param key 缓存的键值 + * @param dataMap 缓存的数据 + */ + public static void setCacheMap(final String key, final Map dataMap) { + if (dataMap != null) { + RMap rMap = CLIENT.getMap(key); + rMap.putAll(dataMap); + } + } + + /** + * 注册Map监听器 + *

+ * key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 + * + * @param key 缓存的键值 + * @param listener 监听器配置 + */ + public static void addMapListener(final String key, final ObjectListener listener) { + RMap rMap = CLIENT.getMap(key); + rMap.addListener(listener); + } + + /** + * 获得缓存的Map + * + * @param key 缓存的键值 + * @return map对象 + */ + public static Map getCacheMap(final String key) { + RMap rMap = CLIENT.getMap(key); + return rMap.getAll(rMap.keySet()); + } + + /** + * 获得缓存Map的key列表 + * + * @param key 缓存的键值 + * @return key列表 + */ + public static Set getCacheMapKeySet(final String key) { + RMap rMap = CLIENT.getMap(key); + return rMap.keySet(); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public static void setCacheMapValue(final String key, final String hKey, final T value) { + RMap rMap = CLIENT.getMap(key); + rMap.put(hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public static T getCacheMapValue(final String key, final String hKey) { + RMap rMap = CLIENT.getMap(key); + return rMap.get(hKey); + } + + /** + * 删除Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public static T delCacheMapValue(final String key, final String hKey) { + RMap rMap = CLIENT.getMap(key); + return rMap.remove(hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public static Map getMultiCacheMapValue(final String key, final Set hKeys) { + RMap rMap = CLIENT.getMap(key); + return rMap.getAll(hKeys); + } + + /** + * 设置原子值 + * + * @param key Redis键 + * @param value 值 + */ + public static void setAtomicValue(String key, long value) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + atomic.set(value); + } + + /** + * 获取原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long getAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.get(); + } + + /** + * 递增原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long incrAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.incrementAndGet(); + } + + /** + * 递减原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long decrAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.decrementAndGet(); + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public static Collection keys(final String pattern) { + Stream stream = CLIENT.getKeys().getKeysStreamByPattern(pattern); + return stream.collect(Collectors.toList()); + } + + /** + * 删除缓存的基本对象列表 + * + * @param pattern 字符串前缀 + */ + public static void deleteKeys(final String pattern) { + CLIENT.getKeys().deleteByPattern(pattern); + } + + /** + * 检查redis中是否存在key + * + * @param key 键 + */ + public static Boolean hasKey(String key) { + RKeys rKeys = CLIENT.getKeys(); + return rKeys.countExists(key) > 0; + } +} diff --git a/service/src/main/java/com/rzdata/common/utils/reflect/ReflectUtils.java b/service/src/main/java/com/rzdata/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..37da2bb --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,56 @@ +package com.rzdata.common.utils.reflect; + +import cn.hutool.core.util.ReflectUtil; +import com.rzdata.common.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.lang.reflect.Method; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author Lion Li + */ +@SuppressWarnings("rawtypes") +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ReflectUtils extends ReflectUtil { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invoke(object, getterMethodName); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) { + if (i < names.length - 1) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invoke(object, getterMethodName); + } else { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + Method method = getMethodByName(object.getClass(), setterMethodName); + invoke(object, method, value); + } + } + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/spring/SpringUtils.java b/service/src/main/java/com/rzdata/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000..d089ba1 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/spring/SpringUtils.java @@ -0,0 +1,74 @@ +package com.rzdata.common.utils.spring; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * spring工具类 + * + * @author Lion Li + */ +@Component +public final class SpringUtils extends SpringUtil { + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return getBeanFactory().containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } + + + /** + * 获取spring上下文 + */ + public static ApplicationContext context() { + return getApplicationContext(); + } + +} diff --git a/service/src/main/java/com/rzdata/common/utils/sql/SqlUtil.java b/service/src/main/java/com/rzdata/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..d8a2a6c --- /dev/null +++ b/service/src/main/java/com/rzdata/common/utils/sql/SqlUtil.java @@ -0,0 +1,57 @@ +package com.rzdata.common.utils.sql; + +import com.rzdata.common.exception.UtilException; +import com.rzdata.common.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * sql操作工具类 + * + * @author rzdata + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SqlUtil { + + /** + * 定义常用的 sql关键字 + */ + public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) { + throw new UtilException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) { + if (StringUtils.isEmpty(value)) { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/service/src/main/java/com/rzdata/common/xss/Xss.java b/service/src/main/java/com/rzdata/common/xss/Xss.java new file mode 100644 index 0000000..21732db --- /dev/null +++ b/service/src/main/java/com/rzdata/common/xss/Xss.java @@ -0,0 +1,26 @@ +package com.rzdata.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author Lion Li + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) +@Constraint(validatedBy = {XssValidator.class}) +public @interface Xss { + + String message() default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/service/src/main/java/com/rzdata/common/xss/XssValidator.java b/service/src/main/java/com/rzdata/common/xss/XssValidator.java new file mode 100644 index 0000000..094c858 --- /dev/null +++ b/service/src/main/java/com/rzdata/common/xss/XssValidator.java @@ -0,0 +1,21 @@ +package com.rzdata.common.xss; + +import cn.hutool.core.util.ReUtil; +import cn.hutool.http.HtmlUtil; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * 自定义xss校验注解实现 + * + * @author Lion Li + */ +public class XssValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/MailController.java b/service/src/main/java/com/rzdata/demo/controller/MailController.java new file mode 100644 index 0000000..d8485b9 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/MailController.java @@ -0,0 +1,52 @@ +package com.rzdata.demo.controller; + +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.email.MailUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; + + +/** + * 邮件发送案例 + * + * @author Michelle.Chung + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/mail") +public class MailController { + + /** + * 发送邮件 + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + */ + @GetMapping("/sendSimpleMessage") + public R sendSimpleMessage(String to, String subject, String text) { + MailUtils.sendText(to, subject, text); + return R.ok(); + } + + /** + * 发送邮件(带附件) + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + * @param filePath 附件路径 + */ + @GetMapping("/sendMessageWithAttachment") + public R sendMessageWithAttachment(String to, String subject, String text, String filePath) { + MailUtils.sendText(to, subject, text, new File(filePath)); + return R.ok(); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/RedisCacheController.java b/service/src/main/java/com/rzdata/demo/controller/RedisCacheController.java new file mode 100644 index 0000000..7a7ab7d --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/RedisCacheController.java @@ -0,0 +1,95 @@ +package com.rzdata.demo.controller; + +import com.rzdata.common.constant.CacheNames; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.redis.RedisUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Duration; + +/** + * spring-cache 演示案例 + * + * @author Lion Li + */ +// 类级别 缓存统一配置 +//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE) +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/cache") +public class RedisCacheController { + + /** + * 测试 @Cacheable + *

+ * 表示这个方法有了缓存的功能,方法的返回值会被缓存下来 + * 下一次调用该方法前,会去检查是否缓存中已经有值 + * 如果有就直接返回,不调用方法 + * 如果没有,就调用方法,然后把结果缓存起来 + * 这个注解「一般用在查询方法上」 + *

+ * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用 + * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null") + @GetMapping("/test1") + public R test1(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试 @CachePut + *

+ * 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用 + * 它「通常用在新增或者实时更新方法上」 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null") + @GetMapping("/test2") + public R test2(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试 @CacheEvict + *

+ * 使用了CacheEvict注解的方法,会清空指定缓存 + * 「一般用在删除的方法上」 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null") + @GetMapping("/test3") + public R test3(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试设置过期时间 + * 手动设置过期时间10秒 + * 11秒后获取 判断是否相等 + */ + @GetMapping("/test6") + public R test6(String key, String value) { + RedisUtils.setCacheObject(key, value); + boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10)); + System.out.println("***********" + flag); + try { + Thread.sleep(11 * 1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Object obj = RedisUtils.getCacheObject(key); + return R.ok(value.equals(obj)); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/RedisLockController.java b/service/src/main/java/com/rzdata/demo/controller/RedisLockController.java new file mode 100644 index 0000000..ac04877 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/RedisLockController.java @@ -0,0 +1,71 @@ +package com.rzdata.demo.controller; + +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; +import com.baomidou.lock.annotation.Lock4j; +import com.baomidou.lock.executor.RedissonLockExecutor; +import com.rzdata.common.core.domain.R; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalTime; + + +/** + * 测试分布式锁的样例 + * + * @author shenxinquan + */ +@Slf4j +@RestController +@RequestMapping("/demo/redisLock") +public class RedisLockController { + + @Autowired + private LockTemplate lockTemplate; + + /** + * 测试lock4j 注解 + */ + @Lock4j(keys = {"#key"}) + @GetMapping("/testLock4j") + public R testLock4j(String key, String value) { + System.out.println("start:" + key + ",time:" + LocalTime.now().toString()); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("end :" + key + ",time:" + LocalTime.now().toString()); + return R.ok("操作成功", value); + } + + /** + * 测试lock4j 工具 + */ + @GetMapping("/testLock4jLockTemplate") + public R testLock4jLockTemplate(String key, String value) { + final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class); + if (null == lockInfo) { + throw new RuntimeException("业务处理中,请稍后再试"); + } + // 获取锁成功,处理业务 + try { + try { + Thread.sleep(8000); + } catch (InterruptedException e) { + // + } + System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName()); + } finally { + //释放锁 + lockTemplate.releaseLock(lockInfo); + } + //结束 + return R.ok("操作成功", value); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/RedisPubSubController.java b/service/src/main/java/com/rzdata/demo/controller/RedisPubSubController.java new file mode 100644 index 0000000..56360d1 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/RedisPubSubController.java @@ -0,0 +1,47 @@ +package com.rzdata.demo.controller; + +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.redis.RedisUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Redis 发布订阅 演示案例 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/redis/pubsub") +public class RedisPubSubController { + + /** + * 发布消息 + * + * @param key 通道Key + * @param value 发送内容 + */ + @GetMapping("/pub") + public R pub(String key, String value) { + RedisUtils.publish(key, value, consumer -> { + System.out.println("发布通道 => " + key + ", 发送值 => " + value); + }); + return R.ok("操作成功"); + } + + /** + * 订阅消息 + * + * @param key 通道Key + */ + @GetMapping("/sub") + public R sub(String key) { + RedisUtils.subscribe(key, String.class, msg -> { + System.out.println("订阅通道 => " + key + ", 接收值 => " + msg); + }); + return R.ok("操作成功"); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/RedisRateLimiterController.java b/service/src/main/java/com/rzdata/demo/controller/RedisRateLimiterController.java new file mode 100644 index 0000000..eabb000 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/RedisRateLimiterController.java @@ -0,0 +1,52 @@ +package com.rzdata.demo.controller; + +import com.rzdata.common.annotation.RateLimiter; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.enums.LimitType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * 测试分布式限流样例 + * + * @author Lion Li + */ +@Slf4j +@RestController +@RequestMapping("/demo/rateLimiter") +public class RedisRateLimiterController { + + /** + * 测试全局限流 + * 全局影响 + */ + @RateLimiter(count = 2, time = 10) + @GetMapping("/test") + public R test(String value) { + return R.ok("操作成功", value); + } + + /** + * 测试请求IP限流 + * 同一IP请求受影响 + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.IP) + @GetMapping("/testip") + public R testip(String value) { + return R.ok("操作成功", value); + } + + /** + * 测试集群实例限流 + * 启动两个后端服务互不影响 + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER) + @GetMapping("/testcluster") + public R testcluster(String value) { + return R.ok("操作成功", value); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/SmsController.java b/service/src/main/java/com/rzdata/demo/controller/SmsController.java new file mode 100644 index 0000000..a01a1d6 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/SmsController.java @@ -0,0 +1,76 @@ +package com.rzdata.demo.controller; + +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.spring.SpringUtils; +import com.rzdata.sms.config.properties.SmsProperties; +import com.rzdata.sms.core.SmsTemplate; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +/** + * 短信演示案例 + * 请先阅读文档 否则无法使用 + * + * @author Lion Li + * @version 4.2.0 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/sms") +public class SmsController { + + private final SmsProperties smsProperties; +// private final SmsTemplate smsTemplate; // 可以使用spring注入 +// private final AliyunSmsTemplate smsTemplate; // 也可以注入某个厂家的模板工具 + + /** + * 发送短信Aliyun + * + * @param phones 电话号 + * @param templateId 模板ID + */ + @GetMapping("/sendAliyun") + public R sendAliyun(String phones, String templateId) { + if (!smsProperties.getEnabled()) { + return R.fail("当前系统没有开启短信功能!"); + } + if (!SpringUtils.containsBean("aliyunSmsTemplate")) { + return R.fail("阿里云依赖未引入!"); + } + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + Map map = new HashMap<>(1); + map.put("code", "1234"); + Object send = smsTemplate.send(phones, templateId, map); + return R.ok(send); + } + + /** + * 发送短信Tencent + * + * @param phones 电话号 + * @param templateId 模板ID + */ + @GetMapping("/sendTencent") + public R sendTencent(String phones, String templateId) { + if (!smsProperties.getEnabled()) { + return R.fail("当前系统没有开启短信功能!"); + } + if (!SpringUtils.containsBean("tencentSmsTemplate")) { + return R.fail("腾讯云依赖未引入!"); + } + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + Map map = new HashMap<>(1); +// map.put("2", "测试测试"); + map.put("1", "1234"); + Object send = smsTemplate.send(phones, templateId, map); + return R.ok(send); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/Swagger3DemoController.java b/service/src/main/java/com/rzdata/demo/controller/Swagger3DemoController.java new file mode 100644 index 0000000..044b242 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/Swagger3DemoController.java @@ -0,0 +1,31 @@ +package com.rzdata.demo.controller; + +import com.rzdata.common.core.domain.R; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * swagger3 用法示例 + * + * @author Lion Li + */ +@RestController +@RequestMapping("/swagger/demo") +public class Swagger3DemoController { + + /** + * 上传请求 + * 必须使用 @RequestPart 注解标注为文件 + * + * @param file 文件 + */ + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R upload(@RequestPart("file") MultipartFile file) { + return R.ok("操作成功", file.getOriginalFilename()); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/TestBatchController.java b/service/src/main/java/com/rzdata/demo/controller/TestBatchController.java new file mode 100644 index 0000000..1fd29e3 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/TestBatchController.java @@ -0,0 +1,90 @@ +package com.rzdata.demo.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.R; +import com.rzdata.demo.domain.TestDemo; +import com.rzdata.demo.mapper.TestDemoMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * 测试批量方法 + * + * @author Lion Li + * @date 2021-05-30 + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/batch") +public class TestBatchController extends BaseController { + + /** + * 为了便于测试 直接引入mapper + */ + private final TestDemoMapper testDemoMapper; + + /** + * 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大) + *

+ * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度 + */ + @PostMapping("/add") +// @DS("slave") + public R add() { + List list = new ArrayList<>(); + for (int i = 0; i < 1000; i++) { + TestDemo testDemo = new TestDemo(); + testDemo.setOrderNum(-1); + testDemo.setTestKey("批量新增"); + testDemo.setValue("测试新增"); + list.add(testDemo); + } + return toAjax(testDemoMapper.insertBatch(list)); + } + + /** + * 新增或更新 可完美替代 saveOrUpdateBatch 高性能 + *

+ * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度 + */ + @PostMapping("/addOrUpdate") +// @DS("slave") + public R addOrUpdate() { + List list = new ArrayList<>(); + for (int i = 0; i < 1000; i++) { + TestDemo testDemo = new TestDemo(); + testDemo.setOrderNum(-1); + testDemo.setTestKey("批量新增"); + testDemo.setValue("测试新增"); + list.add(testDemo); + } + testDemoMapper.insertBatch(list); + for (int i = 0; i < list.size(); i++) { + TestDemo testDemo = list.get(i); + testDemo.setTestKey("批量新增或修改"); + testDemo.setValue("批量新增或修改"); + if (i % 2 == 0) { + testDemo.setId(null); + } + } + return toAjax(testDemoMapper.insertOrUpdateBatch(list)); + } + + /** + * 删除批量方法 + */ + @DeleteMapping() +// @DS("slave") + public R remove() { + return toAjax(testDemoMapper.delete(new LambdaQueryWrapper() + .eq(TestDemo::getOrderNum, -1L))); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/TestDemoController.java b/service/src/main/java/com/rzdata/demo/controller/TestDemoController.java new file mode 100644 index 0000000..4668557 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/TestDemoController.java @@ -0,0 +1,148 @@ +package com.rzdata.demo.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.bean.BeanUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.annotation.RepeatSubmit; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.core.validate.AddGroup; +import com.rzdata.common.core.validate.EditGroup; +import com.rzdata.common.core.validate.QueryGroup; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.excel.ExcelResult; +import com.rzdata.common.utils.ValidatorUtils; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.demo.domain.TestDemo; +import com.rzdata.demo.domain.bo.TestDemoBo; +import com.rzdata.demo.domain.bo.TestDemoImportVo; +import com.rzdata.demo.domain.vo.TestDemoVo; +import com.rzdata.demo.service.ITestDemoService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * 测试单表Controller + * + * @author Lion Li + * @date 2021-07-26 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/demo") +public class TestDemoController extends BaseController { + + private final ITestDemoService iTestDemoService; + + /** + * 查询测试单表列表 + */ + @SaCheckPermission("demo:demo:list") + @GetMapping("/list") + public TableDataInfo list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) { + return iTestDemoService.queryPageList(bo, pageQuery); + } + + /** + * 自定义分页查询 + */ + @SaCheckPermission("demo:demo:list") + @GetMapping("/page") + public TableDataInfo page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) { + return iTestDemoService.customPageList(bo, pageQuery); + } + + /** + * 导入数据 + * + * @param file 导入文件 + */ + @Log(title = "测试单表", businessType = BusinessType.IMPORT) + @SaCheckPermission("demo:demo:import") + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData(@RequestPart("file") MultipartFile file) throws Exception { + ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true); + List volist = excelResult.getList(); + List list = BeanUtil.copyToList(volist, TestDemo.class); + iTestDemoService.saveBatch(list); + return R.ok(excelResult.getAnalysis()); + } + + /** + * 导出测试单表列表 + */ + @SaCheckPermission("demo:demo:export") + @Log(title = "测试单表", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(@Validated TestDemoBo bo, HttpServletResponse response) { + List list = iTestDemoService.queryList(bo); + // 测试雪花id导出 +// for (TestDemoVo vo : list) { +// vo.setId(1234567891234567893L); +// } + ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response); + } + + /** + * 获取测试单表详细信息 + * + * @param id 测试ID + */ + @SaCheckPermission("demo:demo:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return R.ok(iTestDemoService.queryById(id)); + } + + /** + * 新增测试单表 + */ + @SaCheckPermission("demo:demo:add") + @Log(title = "测试单表", businessType = BusinessType.INSERT) + @RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "{repeat.submit.message}") + @PostMapping() + public R add(@RequestBody TestDemoBo bo) { + // 使用校验工具对标 @Validated(AddGroup.class) 注解 + // 用于在非 Controller 的地方校验对象 + ValidatorUtils.validate(bo, AddGroup.class); + return toAjax(iTestDemoService.insertByBo(bo)); + } + + /** + * 修改测试单表 + */ + @SaCheckPermission("demo:demo:edit") + @Log(title = "测试单表", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) { + return toAjax(iTestDemoService.updateByBo(bo)); + } + + /** + * 删除测试单表 + * + * @param ids 测试ID串 + */ + @SaCheckPermission("demo:demo:remove") + @Log(title = "测试单表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true)); + } +} diff --git a/service/src/main/java/com/rzdata/demo/controller/TestExcelController.java b/service/src/main/java/com/rzdata/demo/controller/TestExcelController.java new file mode 100644 index 0000000..976b30b --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/TestExcelController.java @@ -0,0 +1,97 @@ +package com.rzdata.demo.controller; + +import cn.hutool.core.collection.CollUtil; +import com.rzdata.common.utils.poi.ExcelUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 测试Excel功能 + * + * @author Lion Li + */ +@RestController +@RequestMapping("/demo/excel") +public class TestExcelController { + + /** + * 单列表多数据 + */ + @GetMapping("/exportTemplateOne") + public void exportTemplateOne(HttpServletResponse response) { + Map map = new HashMap<>(); + map.put("title", "单列表多数据"); + map.put("test1", "数据测试1"); + map.put("test2", "数据测试2"); + map.put("test3", "数据测试3"); + map.put("test4", "数据测试4"); + map.put("testTest", "666"); + List list = new ArrayList<>(); + list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4")); + list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8")); + list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12")); + ExcelUtil.exportTemplate(CollUtil.newArrayList(map, list), "单列表.xlsx", "excel/单列表.xlsx", response); + } + + /** + * 多列表多数据 + */ + @GetMapping("/exportTemplateMuliti") + public void exportTemplateMuliti(HttpServletResponse response) { + Map map = new HashMap<>(); + map.put("title1", "标题1"); + map.put("title2", "标题2"); + map.put("title3", "标题3"); + map.put("title4", "标题4"); + map.put("author", "Lion Li"); + List list1 = new ArrayList<>(); + list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3")); + list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6")); + list1.add(new TestObj1("list1测试7", "list1测试8", "list1测试9")); + List list2 = new ArrayList<>(); + list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3")); + list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6")); + List list3 = new ArrayList<>(); + list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3")); + List list4 = new ArrayList<>(); + list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3")); + list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6")); + list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9")); + list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12")); + Map multiListMap = new HashMap<>(); + multiListMap.put("map", map); + multiListMap.put("data1", list1); + multiListMap.put("data2", list2); + multiListMap.put("data3", list3); + multiListMap.put("data4", list4); + ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response); + } + + @Data + @AllArgsConstructor + static class TestObj1 { + private String test1; + private String test2; + private String test3; + } + + @Data + @AllArgsConstructor + static class TestObj { + private String name; + private String list1; + private String list2; + private String list3; + private String list4; + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/TestI18nController.java b/service/src/main/java/com/rzdata/demo/controller/TestI18nController.java new file mode 100644 index 0000000..78f3c27 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/TestI18nController.java @@ -0,0 +1,71 @@ +package com.rzdata.demo.controller; + +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.MessageUtils; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + + +/** + * 测试国际化 + * + * @author Lion Li + */ +@Validated +@RestController +@RequestMapping("/demo/i18n") +public class TestI18nController { + + /** + * 通过code获取国际化内容 + * code为 messages.properties 中的 key + *

+ * 测试使用 user.register.success + * + * @param code 国际化code + */ + @GetMapping() + public R get(String code) { + return R.ok(MessageUtils.message(code)); + } + + /** + * Validator 校验国际化 + * 不传值 分别查看异常返回 + *

+ * 测试使用 not.null + */ + @GetMapping("/test1") + public R test1(@NotBlank(message = "{not.null}") String str) { + return R.ok(str); + } + + /** + * Bean 校验国际化 + * 不传值 分别查看异常返回 + *

+ * 测试使用 not.null + */ + @GetMapping("/test2") + public R test2(@Validated TestI18nBo bo) { + return R.ok(bo); + } + + @Data + public static class TestI18nBo { + + @NotBlank(message = "{not.null}") + private String name; + + @NotNull(message = "{not.null}") + @Range(min = 0, max = 100, message = "{length.not.valid}") + private Integer age; + } +} diff --git a/service/src/main/java/com/rzdata/demo/controller/TestSensitiveController.java b/service/src/main/java/com/rzdata/demo/controller/TestSensitiveController.java new file mode 100644 index 0000000..cc1f957 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/TestSensitiveController.java @@ -0,0 +1,75 @@ +package com.rzdata.demo.controller; + +import com.rzdata.common.annotation.Sensitive; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.enums.SensitiveStrategy; +import lombok.Data; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 测试数据脱敏控制器 + *

+ * 默认管理员不过滤 + * 需自行根据业务重写实现 + * + * @author Lion Li + * @version 3.6.0 + * @see com.rzdata.common.core.service.SensitiveService + */ +@RestController +@RequestMapping("/demo/sensitive") +public class TestSensitiveController extends BaseController { + + /** + * 测试数据脱敏 + */ + @GetMapping("/test") + public R test() { + TestSensitive testSensitive = new TestSensitive(); + testSensitive.setIdCard("210397198608215431"); + testSensitive.setPhone("17640125371"); + testSensitive.setAddress("北京市朝阳区某某四合院1203室"); + testSensitive.setEmail("17640125371@163.com"); + testSensitive.setBankCard("6226456952351452853"); + return R.ok(testSensitive); + } + + @Data + static class TestSensitive { + + /** + * 身份证 + */ + @Sensitive(strategy = SensitiveStrategy.ID_CARD) + private String idCard; + + /** + * 电话 + */ + @Sensitive(strategy = SensitiveStrategy.PHONE) + private String phone; + + /** + * 地址 + */ + @Sensitive(strategy = SensitiveStrategy.ADDRESS) + private String address; + + /** + * 邮箱 + */ + @Sensitive(strategy = SensitiveStrategy.EMAIL) + private String email; + + /** + * 银行卡 + */ + @Sensitive(strategy = SensitiveStrategy.BANK_CARD) + private String bankCard; + + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/TestTreeController.java b/service/src/main/java/com/rzdata/demo/controller/TestTreeController.java new file mode 100644 index 0000000..74fac6e --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/TestTreeController.java @@ -0,0 +1,107 @@ +package com.rzdata.demo.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.annotation.RepeatSubmit; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.validate.AddGroup; +import com.rzdata.common.core.validate.EditGroup; +import com.rzdata.common.core.validate.QueryGroup; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.demo.domain.bo.TestTreeBo; +import com.rzdata.demo.domain.vo.TestTreeVo; +import com.rzdata.demo.service.ITestTreeService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +/** + * 测试树表Controller + * + * @author Lion Li + * @date 2021-07-26 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/tree") +public class TestTreeController extends BaseController { + + private final ITestTreeService iTestTreeService; + + /** + * 查询测试树表列表 + */ + @SaCheckPermission("demo:tree:list") + @GetMapping("/list") + public R> list(@Validated(QueryGroup.class) TestTreeBo bo) { + List list = iTestTreeService.queryList(bo); + return R.ok(list); + } + + /** + * 导出测试树表列表 + */ + @SaCheckPermission("demo:tree:export") + @Log(title = "测试树表", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(@Validated TestTreeBo bo, HttpServletResponse response) { + List list = iTestTreeService.queryList(bo); + ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response); + } + + /** + * 获取测试树表详细信息 + * + * @param id 测试树ID + */ + @SaCheckPermission("demo:tree:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return R.ok(iTestTreeService.queryById(id)); + } + + /** + * 新增测试树表 + */ + @SaCheckPermission("demo:tree:add") + @Log(title = "测试树表", businessType = BusinessType.INSERT) + @RepeatSubmit + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) { + return toAjax(iTestTreeService.insertByBo(bo)); + } + + /** + * 修改测试树表 + */ + @SaCheckPermission("demo:tree:edit") + @Log(title = "测试树表", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) { + return toAjax(iTestTreeService.updateByBo(bo)); + } + + /** + * 删除测试树表 + * + * @param ids 测试树ID串 + */ + @SaCheckPermission("demo:tree:remove") + @Log(title = "测试树表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(iTestTreeService.deleteWithValidByIds(Arrays.asList(ids), true)); + } +} diff --git a/service/src/main/java/com/rzdata/demo/controller/package-info.java b/service/src/main/java/com/rzdata/demo/controller/package-info.java new file mode 100644 index 0000000..8b1a15a --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/package-info.java @@ -0,0 +1 @@ +package com.rzdata.demo.controller; diff --git a/service/src/main/java/com/rzdata/demo/controller/queue/BoundedQueueController.java b/service/src/main/java/com/rzdata/demo/controller/queue/BoundedQueueController.java new file mode 100644 index 0000000..a277838 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/queue/BoundedQueueController.java @@ -0,0 +1,90 @@ +package com.rzdata.demo.controller.queue; + +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.redis.QueueUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 有界队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + *

+ * 集群测试通过 同一个数据只会被消费一次 做好事务补偿 + * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条 + * + * @author Lion Li + * @version 3.6.0 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/bounded") +public class BoundedQueueController { + + + /** + * 添加队列数据 + * + * @param queueName 队列名 + * @param capacity 容量 + */ + @GetMapping("/add") + public R add(String queueName, int capacity) { + // 用完了一定要销毁 否则会一直存在 + boolean b = QueueUtils.destroyQueue(queueName); + log.info("通道: {} , 删除: {}", queueName, b); + // 初始化设置一次即可 + if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) { + log.info("通道: {} , 设置容量: {}", queueName, capacity); + } else { + log.info("通道: {} , 设置容量失败", queueName); + return R.fail("操作失败"); + } + for (int i = 0; i < 11; i++) { + String data = "data-" + i; + boolean flag = QueueUtils.addBoundedQueueObject(queueName, data); + if (flag == false) { + log.info("通道: {} , 发送数据: {} 失败, 通道已满", queueName, data); + } else { + log.info("通道: {} , 发送数据: {}", queueName, data); + } + } + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/remove") + public R remove(String queueName) { + String data = "data-" + 5; + if (QueueUtils.removeQueueObject(queueName, data)) { + log.info("通道: {} , 删除数据: {}", queueName, data); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 获取队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/get") + public R get(String queueName) { + String data; + do { + data = QueueUtils.getQueueObject(queueName); + log.info("通道: {} , 获取数据: {}", queueName, data); + } while (data != null); + return R.ok("操作成功"); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/queue/DelayedQueueController.java b/service/src/main/java/com/rzdata/demo/controller/queue/DelayedQueueController.java new file mode 100644 index 0000000..1fe50cb --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/queue/DelayedQueueController.java @@ -0,0 +1,90 @@ +package com.rzdata.demo.controller.queue; + +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.redis.QueueUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.TimeUnit; + +/** + * 延迟队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + * 例如: 创建订单30分钟后过期处理 + *

+ * 集群测试通过 同一个数据只会被消费一次 做好事务补偿 + * 集群测试流程 两台集群分别开启订阅 在其中一台发送数据 观察接收消息的规律 + * + * @author Lion Li + * @version 3.6.0 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/delayed") +public class DelayedQueueController { + + /** + * 订阅队列 + * + * @param queueName 队列名 + */ + @GetMapping("/subscribe") + public R subscribe(String queueName) { + log.info("通道: {} 监听中......", queueName); + // 项目初始化设置一次即可 + QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> { + // 观察接收时间 + log.info("通道: {}, 收到数据: {}", queueName, orderNum); + }); + return R.ok("操作成功"); + } + + /** + * 添加队列数据 + * + * @param queueName 队列名 + * @param orderNum 订单号 + * @param time 延迟时间(秒) + */ + @GetMapping("/add") + public R add(String queueName, String orderNum, Long time) { + QueueUtils.addDelayedQueueObject(queueName, orderNum, time, TimeUnit.SECONDS); + // 观察发送时间 + log.info("通道: {} , 发送数据: {}", queueName, orderNum); + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + * @param orderNum 订单号 + */ + @GetMapping("/remove") + public R remove(String queueName, String orderNum) { + if (QueueUtils.removeDelayedQueueObject(queueName, orderNum)) { + log.info("通道: {} , 删除数据: {}", queueName, orderNum); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 销毁队列 + * + * @param queueName 队列名 + */ + @GetMapping("/destroy") + public R destroy(String queueName) { + // 用完了一定要销毁 否则会一直存在 + QueueUtils.destroyDelayedQueue(queueName); + return R.ok("操作成功"); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/controller/queue/PriorityDemo.java b/service/src/main/java/com/rzdata/demo/controller/queue/PriorityDemo.java new file mode 100644 index 0000000..2cf2022 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/queue/PriorityDemo.java @@ -0,0 +1,22 @@ +package com.rzdata.demo.controller.queue; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 实体类 注意不允许使用内部类 否则会找不到类 + * + * @author Lion Li + * @version 3.6.0 + */ +@Data +@NoArgsConstructor +public class PriorityDemo implements Comparable { + private String name; + private Integer orderNum; + + @Override + public int compareTo(PriorityDemo other) { + return Integer.compare(getOrderNum(), other.getOrderNum()); + } +} diff --git a/service/src/main/java/com/rzdata/demo/controller/queue/PriorityQueueController.java b/service/src/main/java/com/rzdata/demo/controller/queue/PriorityQueueController.java new file mode 100644 index 0000000..6ec1542 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/controller/queue/PriorityQueueController.java @@ -0,0 +1,89 @@ +package com.rzdata.demo.controller.queue; + +import cn.hutool.core.util.RandomUtil; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.redis.QueueUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 优先队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + *

+ * 集群测试通过 同一个消息只会被消费一次 做好事务补偿 + * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条 + * + * @author Lion Li + * @version 3.6.0 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/priority") +public class PriorityQueueController { + + /** + * 添加队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/add") + public R add(String queueName) { + // 用完了一定要销毁 否则会一直存在 + boolean b = QueueUtils.destroyQueue(queueName); + log.info("通道: {} , 删除: {}", queueName, b); + + for (int i = 0; i < 10; i++) { + int randomNum = RandomUtil.randomInt(10); + PriorityDemo data = new PriorityDemo(); + data.setName("data-" + i); + data.setOrderNum(randomNum); + if (QueueUtils.addPriorityQueueObject(queueName, data)) { + log.info("通道: {} , 发送数据: {}", queueName, data); + } else { + log.info("通道: {} , 发送数据: {}, 发送失败", queueName, data); + } + } + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + * @param name 对象名 + * @param orderNum 排序号 + */ + @GetMapping("/remove") + public R remove(String queueName, String name, Integer orderNum) { + PriorityDemo data = new PriorityDemo(); + data.setName(name); + data.setOrderNum(orderNum); + if (QueueUtils.removeQueueObject(queueName, data)) { + log.info("通道: {} , 删除数据: {}", queueName, data); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 获取队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/get") + public R get(String queueName) { + PriorityDemo data; + do { + data = QueueUtils.getQueueObject(queueName); + log.info("通道: {} , 获取数据: {}", queueName, data); + } while (data != null); + return R.ok("操作成功"); + } + +} diff --git a/service/src/main/java/com/rzdata/demo/domain/TestDemo.java b/service/src/main/java/com/rzdata/demo/domain/TestDemo.java new file mode 100644 index 0000000..35c438e --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/domain/TestDemo.java @@ -0,0 +1,66 @@ +package com.rzdata.demo.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 测试单表对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_demo") +public class TestDemo extends BaseEntity { + + private static final long serialVersionUID = 1L; + + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 部门id + */ + private Long deptId; + + /** + * 用户id + */ + private Long userId; + + /** + * 排序号 + */ + @OrderBy(asc = false, sort = 1) + private Integer orderNum; + + /** + * key键 + */ + private String testKey; + + /** + * 值 + */ + private String value; + + /** + * 版本 + */ + @Version + private Long version; + + /** + * 删除标志 + */ + @TableLogic + private Long delFlag; + +} diff --git a/service/src/main/java/com/rzdata/demo/domain/TestTree.java b/service/src/main/java/com/rzdata/demo/domain/TestTree.java new file mode 100644 index 0000000..897f192 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/domain/TestTree.java @@ -0,0 +1,58 @@ +package com.rzdata.demo.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.Version; +import com.rzdata.common.core.domain.TreeEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 测试树表对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_tree") +public class TestTree extends TreeEntity { + + private static final long serialVersionUID = 1L; + + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 部门id + */ + private Long deptId; + + /** + * 用户id + */ + private Long userId; + + /** + * 树节点名 + */ + private String treeName; + + /** + * 版本 + */ + @Version + private Long version; + + /** + * 删除标志 + */ + @TableLogic + private Long delFlag; + +} diff --git a/service/src/main/java/com/rzdata/demo/domain/bo/TestDemoBo.java b/service/src/main/java/com/rzdata/demo/domain/bo/TestDemoBo.java new file mode 100644 index 0000000..00d1a65 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/domain/bo/TestDemoBo.java @@ -0,0 +1,59 @@ +package com.rzdata.demo.domain.bo; + +import com.rzdata.common.core.domain.BaseEntity; +import com.rzdata.common.core.validate.AddGroup; +import com.rzdata.common.core.validate.EditGroup; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 测试单表业务对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class TestDemoBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long userId; + + /** + * 排序号 + */ + @NotNull(message = "排序号不能为空", groups = {AddGroup.class, EditGroup.class}) + private Integer orderNum; + + /** + * key键 + */ + @NotBlank(message = "key键不能为空", groups = {AddGroup.class, EditGroup.class}) + private String testKey; + + /** + * 值 + */ + @NotBlank(message = "值不能为空", groups = {AddGroup.class, EditGroup.class}) + private String value; + +} diff --git a/service/src/main/java/com/rzdata/demo/domain/bo/TestDemoImportVo.java b/service/src/main/java/com/rzdata/demo/domain/bo/TestDemoImportVo.java new file mode 100644 index 0000000..21ef975 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/domain/bo/TestDemoImportVo.java @@ -0,0 +1,53 @@ +package com.rzdata.demo.domain.bo; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 测试单表业务对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +public class TestDemoImportVo { + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空") + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 排序号 + */ + @NotNull(message = "排序号不能为空") + @ExcelProperty(value = "排序号") + private Long orderNum; + + /** + * key键 + */ + @NotBlank(message = "key键不能为空") + @ExcelProperty(value = "key键") + private String testKey; + + /** + * 值 + */ + @NotBlank(message = "值不能为空") + @ExcelProperty(value = "值") + private String value; + +} diff --git a/service/src/main/java/com/rzdata/demo/domain/bo/TestTreeBo.java b/service/src/main/java/com/rzdata/demo/domain/bo/TestTreeBo.java new file mode 100644 index 0000000..ab520c7 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/domain/bo/TestTreeBo.java @@ -0,0 +1,47 @@ +package com.rzdata.demo.domain.bo; + +import com.rzdata.common.core.domain.TreeEntity; +import com.rzdata.common.core.validate.AddGroup; +import com.rzdata.common.core.validate.EditGroup; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 测试树表业务对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class TestTreeBo extends TreeEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long userId; + + /** + * 树节点名 + */ + @NotBlank(message = "树节点名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String treeName; + +} diff --git a/service/src/main/java/com/rzdata/demo/domain/package-info.java b/service/src/main/java/com/rzdata/demo/domain/package-info.java new file mode 100644 index 0000000..d7fd00a --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/domain/package-info.java @@ -0,0 +1 @@ +package com.rzdata.demo.domain; diff --git a/service/src/main/java/com/rzdata/demo/domain/vo/TestDemoVo.java b/service/src/main/java/com/rzdata/demo/domain/vo/TestDemoVo.java new file mode 100644 index 0000000..42e6c7a --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/domain/vo/TestDemoVo.java @@ -0,0 +1,83 @@ +package com.rzdata.demo.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.Date; + + +/** + * 测试单表视图对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@ExcelIgnoreUnannotated +public class TestDemoVo { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 部门id + */ + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 排序号 + */ + @ExcelProperty(value = "排序号") + private Integer orderNum; + + /** + * key键 + */ + @ExcelProperty(value = "key键") + private String testKey; + + /** + * 值 + */ + @ExcelProperty(value = "值") + private String value; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 创建人 + */ + @ExcelProperty(value = "创建人") + private String createBy; + + /** + * 更新时间 + */ + @ExcelProperty(value = "更新时间") + private Date updateTime; + + /** + * 更新人 + */ + @ExcelProperty(value = "更新人") + private String updateBy; + + +} diff --git a/service/src/main/java/com/rzdata/demo/domain/vo/TestTreeVo.java b/service/src/main/java/com/rzdata/demo/domain/vo/TestTreeVo.java new file mode 100644 index 0000000..7f1d8a8 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/domain/vo/TestTreeVo.java @@ -0,0 +1,58 @@ +package com.rzdata.demo.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.Date; + + +/** + * 测试树表视图对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@ExcelIgnoreUnannotated +public class TestTreeVo { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 父id + */ + @ExcelProperty(value = "父id") + private Long parentId; + + /** + * 部门id + */ + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 树节点名 + */ + @ExcelProperty(value = "树节点名") + private String treeName; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + +} diff --git a/service/src/main/java/com/rzdata/demo/mapper/TestDemoMapper.java b/service/src/main/java/com/rzdata/demo/mapper/TestDemoMapper.java new file mode 100644 index 0000000..df06007 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/mapper/TestDemoMapper.java @@ -0,0 +1,58 @@ +package com.rzdata.demo.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.annotation.DataColumn; +import com.rzdata.common.annotation.DataPermission; +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.demo.domain.TestDemo; +import com.rzdata.demo.domain.vo.TestDemoVo; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +/** + * 测试单表Mapper接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface TestDemoMapper extends BaseMapperPlus { + + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + Page customPageList(@Param("page") Page page, @Param("ew") Wrapper wrapper); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) +

> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + int updateById(@Param(Constants.ENTITY) TestDemo entity); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + int deleteBatchIds(@Param(Constants.COLL) Collection idList); +} diff --git a/service/src/main/java/com/rzdata/demo/mapper/TestTreeMapper.java b/service/src/main/java/com/rzdata/demo/mapper/TestTreeMapper.java new file mode 100644 index 0000000..2c57e8d --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/mapper/TestTreeMapper.java @@ -0,0 +1,21 @@ +package com.rzdata.demo.mapper; + +import com.rzdata.common.annotation.DataColumn; +import com.rzdata.common.annotation.DataPermission; +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.demo.domain.TestTree; +import com.rzdata.demo.domain.vo.TestTreeVo; + +/** + * 测试树表Mapper接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +@DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") +}) +public interface TestTreeMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/demo/mapper/package-info.java b/service/src/main/java/com/rzdata/demo/mapper/package-info.java new file mode 100644 index 0000000..87a450f --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/mapper/package-info.java @@ -0,0 +1 @@ +package com.rzdata.demo.mapper; diff --git a/service/src/main/java/com/rzdata/demo/service/ITestDemoService.java b/service/src/main/java/com/rzdata/demo/service/ITestDemoService.java new file mode 100644 index 0000000..c2a92e6 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/service/ITestDemoService.java @@ -0,0 +1,71 @@ +package com.rzdata.demo.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.demo.domain.TestDemo; +import com.rzdata.demo.domain.bo.TestDemoBo; +import com.rzdata.demo.domain.vo.TestDemoVo; + +import java.util.Collection; +import java.util.List; + +/** + * 测试单表Service接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface ITestDemoService { + + /** + * 查询单个 + * + * @return + */ + TestDemoVo queryById(Long id); + + /** + * 查询列表 + */ + TableDataInfo queryPageList(TestDemoBo bo, PageQuery pageQuery); + + /** + * 自定义分页查询 + */ + TableDataInfo customPageList(TestDemoBo bo, PageQuery pageQuery); + + /** + * 查询列表 + */ + List queryList(TestDemoBo bo); + + /** + * 根据新增业务对象插入测试单表 + * + * @param bo 测试单表新增业务对象 + * @return + */ + Boolean insertByBo(TestDemoBo bo); + + /** + * 根据编辑业务对象修改测试单表 + * + * @param bo 测试单表编辑业务对象 + * @return + */ + Boolean updateByBo(TestDemoBo bo); + + /** + * 校验并删除数据 + * + * @param ids 主键集合 + * @param isValid 是否校验,true-删除前校验,false-不校验 + * @return + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 批量保存 + */ + Boolean saveBatch(List list); +} diff --git a/service/src/main/java/com/rzdata/demo/service/ITestTreeService.java b/service/src/main/java/com/rzdata/demo/service/ITestTreeService.java new file mode 100644 index 0000000..b1b4b75 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/service/ITestTreeService.java @@ -0,0 +1,52 @@ +package com.rzdata.demo.service; + +import com.rzdata.demo.domain.bo.TestTreeBo; +import com.rzdata.demo.domain.vo.TestTreeVo; + +import java.util.Collection; +import java.util.List; + +/** + * 测试树表Service接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface ITestTreeService { + /** + * 查询单个 + * + * @return + */ + TestTreeVo queryById(Long id); + + /** + * 查询列表 + */ + List queryList(TestTreeBo bo); + + /** + * 根据新增业务对象插入测试树表 + * + * @param bo 测试树表新增业务对象 + * @return + */ + Boolean insertByBo(TestTreeBo bo); + + /** + * 根据编辑业务对象修改测试树表 + * + * @param bo 测试树表编辑业务对象 + * @return + */ + Boolean updateByBo(TestTreeBo bo); + + /** + * 校验并删除数据 + * + * @param ids 主键集合 + * @param isValid 是否校验,true-删除前校验,false-不校验 + * @return + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/service/src/main/java/com/rzdata/demo/service/impl/TestDemoServiceImpl.java b/service/src/main/java/com/rzdata/demo/service/impl/TestDemoServiceImpl.java new file mode 100644 index 0000000..e117d1e --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/service/impl/TestDemoServiceImpl.java @@ -0,0 +1,110 @@ +package com.rzdata.demo.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.demo.domain.TestDemo; +import com.rzdata.demo.domain.bo.TestDemoBo; +import com.rzdata.demo.domain.vo.TestDemoVo; +import com.rzdata.demo.mapper.TestDemoMapper; +import com.rzdata.demo.service.ITestDemoService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 测试单表Service业务层处理 + * + * @author Lion Li + * @date 2021-07-26 + */ +@RequiredArgsConstructor +@Service +public class TestDemoServiceImpl implements ITestDemoService { + + private final TestDemoMapper baseMapper; + + @Override + public TestDemoVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + @Override + public TableDataInfo queryPageList(TestDemoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 自定义分页查询 + */ + @Override + public TableDataInfo customPageList(TestDemoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.customPageList(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List queryList(TestDemoBo bo) { + return baseMapper.selectVoList(buildQueryWrapper(bo)); + } + + private LambdaQueryWrapper buildQueryWrapper(TestDemoBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey()); + lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + return lqw; + } + + @Override + public Boolean insertByBo(TestDemoBo bo) { + TestDemo add = BeanUtil.toBean(bo, TestDemo.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + @Override + public Boolean updateByBo(TestDemoBo bo) { + TestDemo update = BeanUtil.toBean(bo, TestDemo.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + * + * @param entity 实体类数据 + */ + private void validEntityBeforeSave(TestDemo entity) { + //TODO 做一些数据校验,如唯一约束 + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + @Override + public Boolean saveBatch(List list) { + return baseMapper.insertBatch(list); + } +} diff --git a/service/src/main/java/com/rzdata/demo/service/impl/TestTreeServiceImpl.java b/service/src/main/java/com/rzdata/demo/service/impl/TestTreeServiceImpl.java new file mode 100644 index 0000000..3360ccd --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/service/impl/TestTreeServiceImpl.java @@ -0,0 +1,87 @@ +package com.rzdata.demo.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.demo.domain.TestTree; +import com.rzdata.demo.domain.bo.TestTreeBo; +import com.rzdata.demo.domain.vo.TestTreeVo; +import com.rzdata.demo.mapper.TestTreeMapper; +import com.rzdata.demo.service.ITestTreeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 测试树表Service业务层处理 + * + * @author Lion Li + * @date 2021-07-26 + */ +// @DS("slave") // 切换从库查询 +@RequiredArgsConstructor +@Service +public class TestTreeServiceImpl implements ITestTreeService { + + private final TestTreeMapper baseMapper; + + @Override + public TestTreeVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + // @DS("slave") // 切换从库查询 + @Override + public List queryList(TestTreeBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(TestTreeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + return lqw; + } + + @Override + public Boolean insertByBo(TestTreeBo bo) { + TestTree add = BeanUtil.toBean(bo, TestTree.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + @Override + public Boolean updateByBo(TestTreeBo bo) { + TestTree update = BeanUtil.toBean(bo, TestTree.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + * + * @param entity 实体类数据 + */ + private void validEntityBeforeSave(TestTree entity) { + //TODO 做一些数据校验,如唯一约束 + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/service/src/main/java/com/rzdata/demo/service/impl/package-info.java b/service/src/main/java/com/rzdata/demo/service/impl/package-info.java new file mode 100644 index 0000000..506078c --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/service/impl/package-info.java @@ -0,0 +1 @@ +package com.rzdata.demo.service.impl; diff --git a/service/src/main/java/com/rzdata/demo/service/package-info.java b/service/src/main/java/com/rzdata/demo/service/package-info.java new file mode 100644 index 0000000..a27d252 --- /dev/null +++ b/service/src/main/java/com/rzdata/demo/service/package-info.java @@ -0,0 +1 @@ +package com.rzdata.demo.service; diff --git a/service/src/main/java/com/rzdata/framework/Interceptor/PlusDataPermissionInterceptor.java b/service/src/main/java/com/rzdata/framework/Interceptor/PlusDataPermissionInterceptor.java new file mode 100644 index 0000000..e896b3c --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/Interceptor/PlusDataPermissionInterceptor.java @@ -0,0 +1,108 @@ +package com.rzdata.framework.interceptor; + +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; +import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport; +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; +import com.rzdata.framework.handler.PlusDataPermissionHandler; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectBody; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.update.Update; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +/** + * 数据权限拦截器 + * + * @author Lion Li + * @version 3.5.0 + */ +public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { + + private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); + + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + // 检查忽略注解 + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + // 检查是否无效 无数据权限注解 + if (dataPermissionHandler.isInvalid(ms.getId())) { + return; + } + // 解析 sql 分配对应方法 + PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); + mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); + } + + @Override + public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { + PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); + MappedStatement ms = mpSh.mappedStatement(); + SqlCommandType sct = ms.getSqlCommandType(); + if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); + mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); + } + } + + @Override + protected void processSelect(Select select, int index, String sql, Object obj) { + SelectBody selectBody = select.getSelectBody(); + if (selectBody instanceof PlainSelect) { + this.setWhere((PlainSelect) selectBody, (String) obj); + } else if (selectBody instanceof SetOperationList) { + SetOperationList setOperationList = (SetOperationList) selectBody; + List selectBodyList = setOperationList.getSelects(); + selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj)); + } + } + + @Override + protected void processUpdate(Update update, int index, String sql, Object obj) { + Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false); + if (null != sqlSegment) { + update.setWhere(sqlSegment); + } + } + + @Override + protected void processDelete(Delete delete, int index, String sql, Object obj) { + Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false); + if (null != sqlSegment) { + delete.setWhere(sqlSegment); + } + } + + /** + * 设置 where 条件 + * + * @param plainSelect 查询对象 + * @param mappedStatementId 执行方法id + */ + protected void setWhere(PlainSelect plainSelect, String mappedStatementId) { + Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true); + if (null != sqlSegment) { + plainSelect.setWhere(sqlSegment); + } + } + +} + diff --git a/service/src/main/java/com/rzdata/framework/Interceptor/PlusWebInvokeTimeInterceptor.java b/service/src/main/java/com/rzdata/framework/Interceptor/PlusWebInvokeTimeInterceptor.java new file mode 100644 index 0000000..e590ecb --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/Interceptor/PlusWebInvokeTimeInterceptor.java @@ -0,0 +1,94 @@ +package com.rzdata.framework.interceptor; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; +import com.alibaba.ttl.TransmittableThreadLocal; +import com.rzdata.common.filter.RepeatedlyRequestWrapper; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.util.Map; + +/** + * web的调用时间统计拦截器 + * dev环境有效 + * + * @author Lion Li + * @since 3.3.0 + */ +@Slf4j +public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { + + private final String prodProfile = "prod"; + + private final TransmittableThreadLocal invokeTimeTL = new TransmittableThreadLocal<>(); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (!prodProfile.equals(SpringUtils.getActiveProfile())) { + String url = request.getMethod() + " " + request.getRequestURI(); + + // 打印请求参数 + if (isJsonRequest(request)) { + String jsonParam = ""; + if (request instanceof RepeatedlyRequestWrapper) { + BufferedReader reader = request.getReader(); + jsonParam = IoUtil.read(reader); + } + log.debug("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); + } else { + Map parameterMap = request.getParameterMap(); + if (MapUtil.isNotEmpty(parameterMap)) { + String parameters = JsonUtils.toJsonString(parameterMap); + log.debug("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); + } else { + log.debug("[PLUS]开始请求 => URL[{}],无参数", url); + } + } + + StopWatch stopWatch = new StopWatch(); + invokeTimeTL.set(stopWatch); + stopWatch.start(); + } + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + if (!prodProfile.equals(SpringUtils.getActiveProfile())) { + StopWatch stopWatch = invokeTimeTL.get(); + stopWatch.stop(); + log.debug("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); + invokeTimeTL.remove(); + } + } + + /** + * 判断本次请求的数据类型是否为json + * + * @param request request + * @return boolean + */ + private boolean isJsonRequest(HttpServletRequest request) { + String contentType = request.getContentType(); + if (contentType != null) { + return StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE); + } + return false; + } + +} diff --git a/service/src/main/java/com/rzdata/framework/aspectj/LogAspect.java b/service/src/main/java/com/rzdata/framework/aspectj/LogAspect.java new file mode 100644 index 0000000..00ac6bb --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/aspectj/LogAspect.java @@ -0,0 +1,192 @@ +package com.rzdata.framework.aspectj; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.core.domain.event.OperLogEvent; +import com.rzdata.common.enums.BusinessStatus; +import com.rzdata.common.enums.HttpMethod; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.ServletUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.Map; + +/** + * 操作日志记录处理 + * + * @author Lion Li + */ +@Slf4j +@Aspect +@Component +public class LogAspect { + + /** + * 排除敏感属性字段 + */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { + + // *========数据库日志=========*// + OperLogEvent operLog = new OperLogEvent(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = ServletUtils.getClientIP(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + operLog.setOperName(LoginHelper.getUsername()); + + if (e != null) { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 发布事件保存数据库 + SpringUtils.context().publishEvent(operLog); + } catch (Exception exp) { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog) throws Exception { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } else { + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); + operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + StringBuilder params = new StringBuilder(); + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + try { + String str = JsonUtils.toJsonString(o); + Dict dict = JsonUtils.parseMap(str); + if (MapUtil.isNotEmpty(dict)) { + MapUtil.removeAny(dict, EXCLUDE_PROPERTIES); + str = JsonUtils.toJsonString(dict); + } + params.append(str).append(" "); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + return params.toString().trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/service/src/main/java/com/rzdata/framework/aspectj/RateLimiterAspect.java b/service/src/main/java/com/rzdata/framework/aspectj/RateLimiterAspect.java new file mode 100644 index 0000000..55243ac --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/aspectj/RateLimiterAspect.java @@ -0,0 +1,66 @@ +package com.rzdata.framework.aspectj; + +import com.rzdata.common.annotation.RateLimiter; +import com.rzdata.common.enums.LimitType; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.MessageUtils; +import com.rzdata.common.utils.ServletUtils; +import com.rzdata.common.utils.redis.RedisUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.redisson.api.RateType; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * 限流处理 + * + * @author Lion Li + */ +@Slf4j +@Aspect +@Component +public class RateLimiterAspect { + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + String combineKey = getCombineKey(rateLimiter, point); + try { + RateType rateType = RateType.OVERALL; + if (rateLimiter.limitType() == LimitType.CLUSTER) { + rateType = RateType.PER_CLIENT; + } + long number = RedisUtils.rateLimiter(combineKey, rateType, count, time); + if (number == -1) { + throw new ServiceException(MessageUtils.message("rate.limiter.message")); + } + log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { + StringBuilder stringBuffer = new StringBuilder(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) { + // 获取请求ip + stringBuffer.append(ServletUtils.getClientIP()).append("-"); + } else if (rateLimiter.limitType() == LimitType.CLUSTER) { + // 获取客户端实例id + stringBuffer.append(RedisUtils.getClient().getId()).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/service/src/main/java/com/rzdata/framework/aspectj/RepeatSubmitAspect.java b/service/src/main/java/com/rzdata/framework/aspectj/RepeatSubmitAspect.java new file mode 100644 index 0000000..8894bd5 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/aspectj/RepeatSubmitAspect.java @@ -0,0 +1,159 @@ +package com.rzdata.framework.aspectj; + +import cn.dev33.satoken.SaManager; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.SecureUtil; +import com.rzdata.common.annotation.RepeatSubmit; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.MessageUtils; +import com.rzdata.common.utils.ServletUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.RedisUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.Duration; +import java.util.Collection; +import java.util.Map; + +/** + * 防止重复提交(参考美团GTIS防重系统) + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor +@Aspect +@Component +public class RepeatSubmitAspect { + + private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); + + @Before("@annotation(repeatSubmit)") + public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable { + // 如果注解不为0 则使用注解数值 + long interval = 0; + if (repeatSubmit.interval() > 0) { + interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval()); + } + if (interval < 1000) { + throw new ServiceException("重复提交间隔时间不能小于'1'秒"); + } + HttpServletRequest request = ServletUtils.getRequest(); + String nowParams = argsArrayToString(point.getArgs()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName())); + + submitKey = SecureUtil.md5(submitKey + ":" + nowParams); + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; + String key = RedisUtils.getCacheObject(cacheRepeatKey); + if (key == null) { + RedisUtils.setCacheObject(cacheRepeatKey, "", Duration.ofMillis(interval)); + KEY_CACHE.set(cacheRepeatKey); + } else { + String message = repeatSubmit.message(); + if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) { + message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1)); + } + throw new ServiceException(message); + } + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) { + if (jsonResult instanceof R) { + try { + R r = (R) jsonResult; + // 成功则不删除redis数据 保证在有效时间内无法重复提交 + if (r.getCode() == R.SUCCESS) { + return; + } + RedisUtils.deleteObject(KEY_CACHE.get()); + } finally { + KEY_CACHE.remove(); + } + } + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) { + RedisUtils.deleteObject(KEY_CACHE.get()); + KEY_CACHE.remove(); + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + StringBuilder params = new StringBuilder(); + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + try { + params.append(JsonUtils.toJsonString(o)).append(" "); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + return params.toString().trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/ApplicationConfig.java b/service/src/main/java/com/rzdata/framework/config/ApplicationConfig.java new file mode 100644 index 0000000..b06219e --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/ApplicationConfig.java @@ -0,0 +1,16 @@ +package com.rzdata.framework.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author Lion Li + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +public class ApplicationConfig { + +} diff --git a/service/src/main/java/com/rzdata/framework/config/AsyncConfig.java b/service/src/main/java/com/rzdata/framework/config/AsyncConfig.java new file mode 100644 index 0000000..2982122 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/AsyncConfig.java @@ -0,0 +1,54 @@ +package com.rzdata.framework.config; + +import cn.hutool.core.util.ArrayUtil; +import com.rzdata.common.exception.ServiceException; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurerSupport; +import org.springframework.scheduling.annotation.EnableAsync; + +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; + +/** + * 异步配置 + * + * @author Lion Li + */ +@EnableAsync(proxyTargetClass = true) +@Configuration +public class AsyncConfig extends AsyncConfigurerSupport { + + @Autowired + @Qualifier("scheduledExecutorService") + private ScheduledExecutorService scheduledExecutorService; + + /** + * 自定义 @Async 注解使用系统线程池 + */ + @Override + public Executor getAsyncExecutor() { + return scheduledExecutorService; + } + + /** + * 异步执行异常处理 + */ + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return (throwable, method, objects) -> { + throwable.printStackTrace(); + StringBuilder sb = new StringBuilder(); + sb.append("Exception message - ").append(throwable.getMessage()) + .append(", Method name - ").append(method.getName()); + if (ArrayUtil.isNotEmpty(objects)) { + sb.append(", Parameter value - ").append(Arrays.toString(objects)); + } + throw new ServiceException(sb.toString()); + }; + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/CaptchaConfig.java b/service/src/main/java/com/rzdata/framework/config/CaptchaConfig.java new file mode 100644 index 0000000..57eb19d --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/CaptchaConfig.java @@ -0,0 +1,62 @@ +package com.rzdata.framework.config; + +import cn.hutool.captcha.CaptchaUtil; +import cn.hutool.captcha.CircleCaptcha; +import cn.hutool.captcha.LineCaptcha; +import cn.hutool.captcha.ShearCaptcha; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +import java.awt.*; + +/** + * 验证码配置 + * + * @author Lion Li + */ +@Configuration +public class CaptchaConfig { + + private static final int WIDTH = 160; + private static final int HEIGHT = 60; + private static final Color BACKGROUND = Color.PINK; + private static final Font FONT = new Font("Arial", Font.BOLD, 48); + + /** + * 圆圈干扰验证码 + */ + @Lazy + @Bean + public CircleCaptcha circleCaptcha() { + CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT); + captcha.setBackground(BACKGROUND); + captcha.setFont(FONT); + return captcha; + } + + /** + * 线段干扰的验证码 + */ + @Lazy + @Bean + public LineCaptcha lineCaptcha() { + LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT); + captcha.setBackground(BACKGROUND); + captcha.setFont(FONT); + return captcha; + } + + /** + * 扭曲干扰验证码 + */ + @Lazy + @Bean + public ShearCaptcha shearCaptcha() { + ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT); + captcha.setBackground(BACKGROUND); + captcha.setFont(FONT); + return captcha; + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/FilterConfig.java b/service/src/main/java/com/rzdata/framework/config/FilterConfig.java new file mode 100644 index 0000000..e702bdc --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/FilterConfig.java @@ -0,0 +1,55 @@ +package com.rzdata.framework.config; + +import com.rzdata.common.filter.RepeatableFilter; +import com.rzdata.common.filter.XssFilter; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.framework.config.properties.XssProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.DispatcherType; +import java.util.HashMap; +import java.util.Map; + +/** + * Filter配置 + * + * @author Lion Li + */ +@Configuration +public class FilterConfig { + + @Autowired + private XssProperties xssProperties; + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", xssProperties.getExcludes()); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + public FilterRegistrationBean someFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/I18nConfig.java b/service/src/main/java/com/rzdata/framework/config/I18nConfig.java new file mode 100644 index 0000000..13c9fe8 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/I18nConfig.java @@ -0,0 +1,46 @@ +package com.rzdata.framework.config; + +import cn.hutool.core.util.StrUtil; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Locale; + +/** + * 国际化配置 + * + * @author Lion Li + */ +@Configuration +public class I18nConfig { + + @Bean + public LocaleResolver localeResolver() { + return new I18nLocaleResolver(); + } + + /** + * 获取请求头国际化信息 + */ + static class I18nLocaleResolver implements LocaleResolver { + + @Override + public Locale resolveLocale(HttpServletRequest httpServletRequest) { + String language = httpServletRequest.getHeader("content-language"); + Locale locale = Locale.getDefault(); + if (StrUtil.isNotBlank(language)) { + String[] split = language.split("_"); + locale = new Locale(split[0], split[1]); + } + return locale; + } + + @Override + public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { + + } + } +} diff --git a/service/src/main/java/com/rzdata/framework/config/JacksonConfig.java b/service/src/main/java/com/rzdata/framework/config/JacksonConfig.java new file mode 100644 index 0000000..57d564d --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/JacksonConfig.java @@ -0,0 +1,46 @@ +package com.rzdata.framework.config; + +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.rzdata.framework.jackson.BigNumberSerializer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.TimeZone; + +/** + * jackson 配置 + * + * @author Lion Li + */ +@Slf4j +@Configuration +public class JacksonConfig { + + @Bean + public Jackson2ObjectMapperBuilderCustomizer customizer() { + return builder -> { + // 全局配置序列化返回 JSON 处理 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); + javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); + builder.modules(javaTimeModule); + builder.timeZone(TimeZone.getDefault()); + log.info("初始化 jackson 配置"); + }; + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/MailConfig.java b/service/src/main/java/com/rzdata/framework/config/MailConfig.java new file mode 100644 index 0000000..77c9feb --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/MailConfig.java @@ -0,0 +1,35 @@ +package com.rzdata.framework.config; + +import cn.hutool.extra.mail.MailAccount; +import com.rzdata.framework.config.properties.MailProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * JavaMail 配置 + * + * @author Michelle.Chung + */ +@Configuration +public class MailConfig { + + @Bean + @ConditionalOnProperty(value = "mail.enabled", havingValue = "true") + public MailAccount mailAccount(MailProperties mailProperties) { + MailAccount account = new MailAccount(); + account.setHost(mailProperties.getHost()); + account.setPort(mailProperties.getPort()); + account.setAuth(mailProperties.getAuth()); + account.setFrom(mailProperties.getFrom()); + account.setUser(mailProperties.getUser()); + account.setPass(mailProperties.getPass()); + account.setSocketFactoryPort(mailProperties.getPort()); + account.setStarttlsEnable(mailProperties.getStarttlsEnable()); + account.setSslEnable(mailProperties.getSslEnable()); + account.setTimeout(mailProperties.getTimeout()); + account.setConnectionTimeout(mailProperties.getConnectionTimeout()); + return account; + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/MybatisPlusConfig.java b/service/src/main/java/com/rzdata/framework/config/MybatisPlusConfig.java new file mode 100644 index 0000000..5eeb24d --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/MybatisPlusConfig.java @@ -0,0 +1,102 @@ +package com.rzdata.framework.config; + +import cn.hutool.core.net.NetUtil; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.rzdata.framework.handler.CreateAndUpdateMetaObjectHandler; +import com.rzdata.framework.interceptor.PlusDataPermissionInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * mybatis-plus配置类(下方注释有插件介绍) + * + * @author Lion Li + */ +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +@MapperScan("${mybatis-plus.mapperPackage}") +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 数据权限处理 + interceptor.addInnerInterceptor(dataPermissionInterceptor()); + // 分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + return interceptor; + } + + /** + * 数据权限拦截器 + */ + public PlusDataPermissionInterceptor dataPermissionInterceptor() { + return new PlusDataPermissionInterceptor(); + } + + /** + * 分页插件,自动识别数据库类型 + */ + public PaginationInnerInterceptor paginationInnerInterceptor() { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 设置最大单页限制数量,默认 500 条,-1 不受限制 + paginationInnerInterceptor.setMaxLimit(-1L); + // 分页合理化 + paginationInnerInterceptor.setOverflow(true); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 元对象字段填充控制器 + */ + @Bean + public MetaObjectHandler metaObjectHandler() { + return new CreateAndUpdateMetaObjectHandler(); + } + + /** + * 使用网卡信息绑定雪花生成器 + * 防止集群雪花ID重复 + */ + @Bean + public IdentifierGenerator idGenerator() { + return new DefaultIdentifierGenerator(NetUtil.getLocalhost()); + } + + /** + * PaginationInnerInterceptor 分页插件,自动识别数据库类型 + * https://baomidou.com/pages/97710a/ + * OptimisticLockerInnerInterceptor 乐观锁插件 + * https://baomidou.com/pages/0d93c0/ + * MetaObjectHandler 元对象字段填充控制器 + * https://baomidou.com/pages/4c6bcf/ + * ISqlInjector sql注入器 + * https://baomidou.com/pages/42ea4a/ + * BlockAttackInnerInterceptor 如果是对全表的删除或更新操作,就会终止该操作 + * https://baomidou.com/pages/f9a237/ + * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截) + * IdentifierGenerator 自定义主键策略 + * https://baomidou.com/pages/568eb2/ + * TenantLineInnerInterceptor 多租户插件 + * https://baomidou.com/pages/aef2f2/ + * DynamicTableNameInnerInterceptor 动态表名插件 + * https://baomidou.com/pages/2a45ff/ + */ + +} diff --git a/service/src/main/java/com/rzdata/framework/config/RedisConfig.java b/service/src/main/java/com/rzdata/framework/config/RedisConfig.java new file mode 100644 index 0000000..5b37867 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/RedisConfig.java @@ -0,0 +1,130 @@ +package com.rzdata.framework.config; + +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.rzdata.framework.config.properties.RedissonProperties; +import com.rzdata.framework.handler.KeyPrefixHandler; +import com.rzdata.framework.manager.PlusSpringCacheManager; +import lombok.extern.slf4j.Slf4j; +import org.redisson.codec.JsonJacksonCodec; +import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * redis配置 + * + * @author Lion Li + */ +@Slf4j +@Configuration +@EnableCaching +@EnableConfigurationProperties(RedissonProperties.class) +public class RedisConfig { + + @Autowired + private RedissonProperties redissonProperties; + + @Autowired + private ObjectMapper objectMapper; + + @Bean + public RedissonAutoConfigurationCustomizer redissonCustomizer() { + return config -> { + config.setThreads(redissonProperties.getThreads()) + .setNettyThreads(redissonProperties.getNettyThreads()) + .setCodec(new JsonJacksonCodec(objectMapper)); + RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); + if (ObjectUtil.isNotNull(singleServerConfig)) { + // 使用单机模式 + config.useSingleServer() + //设置redis key前缀 + .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix())) + .setTimeout(singleServerConfig.getTimeout()) + .setClientName(singleServerConfig.getClientName()) + .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) + .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) + .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) + .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()); + } + // 集群配置方式 参考下方注释 + RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig(); + if (ObjectUtil.isNotNull(clusterServersConfig)) { + config.useClusterServers() + //设置redis key前缀 + .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix())) + .setTimeout(clusterServersConfig.getTimeout()) + .setClientName(clusterServersConfig.getClientName()) + .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout()) + .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize()) + .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize()) + .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize()) + .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize()) + .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize()) + .setReadMode(clusterServersConfig.getReadMode()) + .setSubscriptionMode(clusterServersConfig.getSubscriptionMode()); + } + log.info("初始化 redis 配置"); + }; + } + + /** + * 自定义缓存管理器 整合spring-cache + */ + @Bean + public CacheManager cacheManager() { + return new PlusSpringCacheManager(); + } + + /** + * redis集群配置 yml + * + * --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉) + * spring: + * redis: + * cluster: + * nodes: + * - 192.168.0.100:6379 + * - 192.168.0.101:6379 + * - 192.168.0.102:6379 + * # 密码 + * password: + * # 连接超时时间 + * timeout: 10s + * # 是否开启ssl + * ssl: false + * + * redisson: + * # 线程池数量 + * threads: 16 + * # Netty线程池数量 + * nettyThreads: 32 + * # 集群配置 + * clusterServersConfig: + * # 客户端名称 + * clientName: ${rzdata.name} + * # master最小空闲连接数 + * masterConnectionMinimumIdleSize: 32 + * # master连接池大小 + * masterConnectionPoolSize: 64 + * # slave最小空闲连接数 + * slaveConnectionMinimumIdleSize: 32 + * # slave连接池大小 + * slaveConnectionPoolSize: 64 + * # 连接空闲超时,单位:毫秒 + * idleConnectionTimeout: 10000 + * # 命令等待超时,单位:毫秒 + * timeout: 3000 + * # 发布和订阅连接池大小 + * subscriptionConnectionPoolSize: 50 + * # 读取模式 + * readMode: "SLAVE" + * # 订阅模式 + * subscriptionMode: "MASTER" + */ + +} diff --git a/service/src/main/java/com/rzdata/framework/config/ResourcesConfig.java b/service/src/main/java/com/rzdata/framework/config/ResourcesConfig.java new file mode 100644 index 0000000..a3d8116 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/ResourcesConfig.java @@ -0,0 +1,52 @@ +package com.rzdata.framework.config; + +import com.rzdata.framework.interceptor.PlusWebInvokeTimeInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 通用配置 + * + * @author Lion Li + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 全局访问性能拦截 + registry.addInterceptor(new PlusWebInvokeTimeInterceptor()); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} diff --git a/service/src/main/java/com/rzdata/framework/config/SaTokenConfig.java b/service/src/main/java/com/rzdata/framework/config/SaTokenConfig.java new file mode 100644 index 0000000..c945619 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/SaTokenConfig.java @@ -0,0 +1,62 @@ +package com.rzdata.framework.config; + +import cn.dev33.satoken.interceptor.SaInterceptor; +import cn.dev33.satoken.jwt.StpLogicJwtForSimple; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; +import com.rzdata.framework.config.properties.SecurityProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * sa-token 配置 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Configuration +public class SaTokenConfig implements WebMvcConfigurer { + + private final SecurityProperties securityProperties; + + /** + * 注册sa-token的拦截器 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 注册路由拦截器,自定义验证规则 + registry.addInterceptor(new SaInterceptor(handler -> { + // 登录验证 -- 排除多个路径 + SaRouter + // 获取所有的 + .match("/**") + // 对未排除的路径进行检查 + .check(() -> { + // 检查是否登录 是否有token + StpUtil.checkLogin(); + + // 有效率影响 用于临时测试 + // if (log.isDebugEnabled()) { + // log.debug("剩余有效时间: {}", StpUtil.getTokenTimeout()); + // log.debug("临时有效时间: {}", StpUtil.getTokenActivityTimeout()); + // } + + }); + })).addPathPatterns("/**") + // 排除不需要拦截的路径 + .excludePathPatterns(securityProperties.getExcludes()); + } + + @Bean + public StpLogic getStpLogicJwt() { + // Sa-Token 整合 jwt (简单模式) + return new StpLogicJwtForSimple(); + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/SwaggerConfig.java b/service/src/main/java/com/rzdata/framework/config/SwaggerConfig.java new file mode 100644 index 0000000..d190031 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/SwaggerConfig.java @@ -0,0 +1,123 @@ +package com.rzdata.framework.config; + +import com.rzdata.common.utils.StringUtils; +import com.rzdata.framework.config.properties.SwaggerProperties; +import com.rzdata.framework.handler.OpenApiHandler; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.*; +import org.springdoc.core.customizers.OpenApiBuilderCustomizer; +import org.springdoc.core.customizers.OpenApiCustomiser; +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; +import org.springdoc.core.providers.JavadocProvider; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * Swagger 文档配置 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Configuration +@AutoConfigureBefore(SpringDocConfiguration.class) +@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true", matchIfMissing = true) +public class SwaggerConfig { + + private final SwaggerProperties swaggerProperties; + private final ServerProperties serverProperties; + + @Bean + @ConditionalOnMissingBean(OpenAPI.class) + public OpenAPI openApi() { + OpenAPI openApi = new OpenAPI(); + // 文档基本信息 + SwaggerProperties.InfoProperties infoProperties = swaggerProperties.getInfo(); + Info info = convertInfo(infoProperties); + openApi.info(info); + // 扩展文档信息 + openApi.externalDocs(swaggerProperties.getExternalDocs()); + openApi.tags(swaggerProperties.getTags()); + openApi.paths(swaggerProperties.getPaths()); + openApi.components(swaggerProperties.getComponents()); + Set keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet(); + List list = new ArrayList<>(); + SecurityRequirement securityRequirement = new SecurityRequirement(); + keySet.forEach(securityRequirement::addList); + list.add(securityRequirement); + openApi.security(list); + + return openApi; + } + + private Info convertInfo(SwaggerProperties.InfoProperties infoProperties) { + Info info = new Info(); + info.setTitle(infoProperties.getTitle()); + info.setDescription(infoProperties.getDescription()); + info.setContact(infoProperties.getContact()); + info.setLicense(infoProperties.getLicense()); + info.setVersion(infoProperties.getVersion()); + return info; + } + + /** + * 自定义 openapi 处理器 + */ + @Bean + public OpenAPIService openApiBuilder(Optional openAPI, + SecurityService securityParser, + SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, + Optional> openApiBuilderCustomisers, + Optional> serverBaseUrlCustomisers, Optional javadocProvider) { + return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider); + } + + /** + * 对已经生成好的 OpenApi 进行自定义操作 + */ + @Bean + public OpenApiCustomiser openApiCustomiser() { + String contextPath = serverProperties.getServlet().getContextPath(); + String finalContextPath; + if (StringUtils.isBlank(contextPath) || "/".equals(contextPath)) { + finalContextPath = ""; + } else { + finalContextPath = contextPath; + } + // 对所有路径增加前置上下文路径 + return openApi -> { + Paths oldPaths = openApi.getPaths(); + if (oldPaths instanceof PlusPaths) { + return; + } + PlusPaths newPaths = new PlusPaths(); + oldPaths.forEach((k,v) -> newPaths.addPathItem(finalContextPath + k, v)); + openApi.setPaths(newPaths); + }; + } + + /** + * 单独使用一个类便于判断 解决springdoc路径拼接重复问题 + * + * @author Lion Li + */ + static class PlusPaths extends Paths { + + public PlusPaths() { + super(); + } + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/ThreadPoolConfig.java b/service/src/main/java/com/rzdata/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000..0430da8 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/ThreadPoolConfig.java @@ -0,0 +1,59 @@ +package com.rzdata.framework.config; + +import com.rzdata.common.utils.Threads; +import com.rzdata.framework.config.properties.ThreadPoolProperties; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author Lion Li + **/ +@Configuration +public class ThreadPoolConfig { + + /** + * 核心线程数 = cpu 核心数 + 1 + */ + private final int core = Runtime.getRuntime().availableProcessors() + 1; + + @Autowired + private ThreadPoolProperties threadPoolProperties; + + @Bean(name = "threadPoolTaskExecutor") + @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(core); + executor.setMaxPoolSize(core * 2); + executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); + executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + return new ScheduledThreadPoolExecutor(core, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/service/src/main/java/com/rzdata/framework/config/UndertowConfig.java b/service/src/main/java/com/rzdata/framework/config/UndertowConfig.java new file mode 100644 index 0000000..54456be --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/UndertowConfig.java @@ -0,0 +1,30 @@ +package com.rzdata.framework.config; + +import io.undertow.server.DefaultByteBufferPool; +import io.undertow.websockets.jsr.WebSocketDeploymentInfo; +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.context.annotation.Configuration; + +/** + * Undertow 自定义配置 + * + * @author Lion Li + */ +@Configuration +public class UndertowConfig implements WebServerFactoryCustomizer { + + /** + * 设置 Undertow 的 websocket 缓冲池 + */ + @Override + public void customize(UndertowServletWebServerFactory factory) { + // 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配 + factory.addDeploymentInfoCustomizers(deploymentInfo -> { + WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); + webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512)); + deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); + }); + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/ValidatorConfig.java b/service/src/main/java/com/rzdata/framework/config/ValidatorConfig.java new file mode 100644 index 0000000..94eb88d --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/ValidatorConfig.java @@ -0,0 +1,43 @@ +package com.rzdata.framework.config; + +import org.hibernate.validator.HibernateValidator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +import javax.validation.Validator; +import java.util.Properties; + +/** + * 校验框架配置类 + * + * @author Lion Li + */ +@Configuration +public class ValidatorConfig { + + @Autowired + private MessageSource messageSource; + + /** + * 配置校验框架 快速返回模式 + */ + @Bean + public Validator validator() { + LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); + // 国际化 + factoryBean.setValidationMessageSource(messageSource); + // 设置使用 HibernateValidator 校验器 + factoryBean.setProviderClass(HibernateValidator.class); + Properties properties = new Properties(); + // 设置 快速异常返回 + properties.setProperty("hibernate.validator.fail_fast", "true"); + factoryBean.setValidationProperties(properties); + // 加载配置 + factoryBean.afterPropertiesSet(); + return factoryBean.getValidator(); + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/properties/CaptchaProperties.java b/service/src/main/java/com/rzdata/framework/config/properties/CaptchaProperties.java new file mode 100644 index 0000000..cd33f06 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/properties/CaptchaProperties.java @@ -0,0 +1,38 @@ +package com.rzdata.framework.config.properties; + +import com.rzdata.common.enums.CaptchaCategory; +import com.rzdata.common.enums.CaptchaType; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 验证码 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "captcha") +public class CaptchaProperties { + + /** + * 验证码类型 + */ + private CaptchaType type; + + /** + * 验证码类别 + */ + private CaptchaCategory category; + + /** + * 数字验证码位数 + */ + private Integer numberLength; + + /** + * 字符验证码长度 + */ + private Integer charLength; +} diff --git a/service/src/main/java/com/rzdata/framework/config/properties/MailProperties.java b/service/src/main/java/com/rzdata/framework/config/properties/MailProperties.java new file mode 100644 index 0000000..003415e --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/properties/MailProperties.java @@ -0,0 +1,71 @@ +package com.rzdata.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * JavaMail 配置属性 + * + * @author Michelle.Chung + */ +@Data +@Component +@ConfigurationProperties(prefix = "mail") +public class MailProperties { + + /** + * 过滤开关 + */ + private Boolean enabled; + + /** + * SMTP服务器域名 + */ + private String host; + + /** + * SMTP服务端口 + */ + private Integer port; + + /** + * 是否需要用户名密码验证 + */ + private Boolean auth; + + /** + * 用户名 + */ + private String user; + + /** + * 密码 + */ + private String pass; + + /** + * 发送方,遵循RFC-822标准 + */ + private String from; + + /** + * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + */ + private Boolean starttlsEnable; + + /** + * 使用 SSL安全连接 + */ + private Boolean sslEnable; + + /** + * SMTP超时时长,单位毫秒,缺省值不超时 + */ + private Long timeout; + + /** + * Socket连接超时值,单位毫秒,缺省值不超时 + */ + private Long connectionTimeout; +} diff --git a/service/src/main/java/com/rzdata/framework/config/properties/RedissonProperties.java b/service/src/main/java/com/rzdata/framework/config/properties/RedissonProperties.java new file mode 100644 index 0000000..606ac10 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/properties/RedissonProperties.java @@ -0,0 +1,137 @@ +package com.rzdata.framework.config.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.redisson.config.ReadMode; +import org.redisson.config.SubscriptionMode; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Redisson 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "redisson") +public class RedissonProperties { + + /** + * redis缓存key前缀 + */ + private String keyPrefix; + + /** + * 线程池数量,默认值 = 当前处理核数量 * 2 + */ + private int threads; + + /** + * Netty线程池数量,默认值 = 当前处理核数量 * 2 + */ + private int nettyThreads; + + /** + * 单机服务配置 + */ + private SingleServerConfig singleServerConfig; + + /** + * 集群服务配置 + */ + private ClusterServersConfig clusterServersConfig; + + @Data + @NoArgsConstructor + public static class SingleServerConfig { + + /** + * 客户端名称 + */ + private String clientName; + + /** + * 最小空闲连接数 + */ + private int connectionMinimumIdleSize; + + /** + * 连接池大小 + */ + private int connectionPoolSize; + + /** + * 连接空闲超时,单位:毫秒 + */ + private int idleConnectionTimeout; + + /** + * 命令等待超时,单位:毫秒 + */ + private int timeout; + + /** + * 发布和订阅连接池大小 + */ + private int subscriptionConnectionPoolSize; + + } + + @Data + @NoArgsConstructor + public static class ClusterServersConfig { + + /** + * 客户端名称 + */ + private String clientName; + + /** + * master最小空闲连接数 + */ + private int masterConnectionMinimumIdleSize; + + /** + * master连接池大小 + */ + private int masterConnectionPoolSize; + + /** + * slave最小空闲连接数 + */ + private int slaveConnectionMinimumIdleSize; + + /** + * slave连接池大小 + */ + private int slaveConnectionPoolSize; + + /** + * 连接空闲超时,单位:毫秒 + */ + private int idleConnectionTimeout; + + /** + * 命令等待超时,单位:毫秒 + */ + private int timeout; + + /** + * 发布和订阅连接池大小 + */ + private int subscriptionConnectionPoolSize; + + /** + * 读取模式 + */ + private ReadMode readMode; + + /** + * 订阅模式 + */ + private SubscriptionMode subscriptionMode; + + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/properties/SecurityProperties.java b/service/src/main/java/com/rzdata/framework/config/properties/SecurityProperties.java new file mode 100644 index 0000000..6345ab8 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/properties/SecurityProperties.java @@ -0,0 +1,23 @@ +package com.rzdata.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Security 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "security") +public class SecurityProperties { + + /** + * 排除路径 + */ + private String[] excludes; + + +} diff --git a/service/src/main/java/com/rzdata/framework/config/properties/SwaggerProperties.java b/service/src/main/java/com/rzdata/framework/config/properties/SwaggerProperties.java new file mode 100644 index 0000000..ed55396 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/properties/SwaggerProperties.java @@ -0,0 +1,101 @@ +package com.rzdata.framework.config.properties; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.tags.Tag; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * swagger 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "swagger") +public class SwaggerProperties { + + /** + * 是否开启 openApi 文档 + */ + private Boolean enabled = true; + + /** + * 文档基本信息 + */ + @NestedConfigurationProperty + private InfoProperties info = new InfoProperties(); + + /** + * 扩展文档地址 + */ + @NestedConfigurationProperty + private ExternalDocumentation externalDocs; + + /** + * 标签 + */ + private List tags = null; + + /** + * 路径 + */ + @NestedConfigurationProperty + private Paths paths = null; + + /** + * 组件 + */ + @NestedConfigurationProperty + private Components components = null; + + /** + *

+ * 文档的基础属性信息 + *

+ * + * @see io.swagger.v3.oas.models.info.Info + * + * 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来 + */ + @Data + public static class InfoProperties { + + /** + * 标题 + */ + private String title = null; + + /** + * 描述 + */ + private String description = null; + + /** + * 联系人信息 + */ + @NestedConfigurationProperty + private Contact contact = null; + + /** + * 许可证 + */ + @NestedConfigurationProperty + private License license = null; + + /** + * 版本 + */ + private String version = null; + + } + +} diff --git a/service/src/main/java/com/rzdata/framework/config/properties/ThreadPoolProperties.java b/service/src/main/java/com/rzdata/framework/config/properties/ThreadPoolProperties.java new file mode 100644 index 0000000..0b13b15 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/properties/ThreadPoolProperties.java @@ -0,0 +1,32 @@ +package com.rzdata.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 线程池 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "thread-pool") +public class ThreadPoolProperties { + + /** + * 是否开启线程池 + */ + private boolean enabled; + + /** + * 队列最大长度 + */ + private int queueCapacity; + + /** + * 线程池维护线程所允许的空闲时间 + */ + private int keepAliveSeconds; + +} diff --git a/service/src/main/java/com/rzdata/framework/config/properties/XssProperties.java b/service/src/main/java/com/rzdata/framework/config/properties/XssProperties.java new file mode 100644 index 0000000..9c98dcc --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/config/properties/XssProperties.java @@ -0,0 +1,32 @@ +package com.rzdata.framework.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * xss过滤 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "xss") +public class XssProperties { + + /** + * 过滤开关 + */ + private String enabled; + + /** + * 排除链接(多个用逗号分隔) + */ + private String excludes; + + /** + * 匹配链接 + */ + private String urlPatterns; + +} diff --git a/service/src/main/java/com/rzdata/framework/handler/CreateAndUpdateMetaObjectHandler.java b/service/src/main/java/com/rzdata/framework/handler/CreateAndUpdateMetaObjectHandler.java new file mode 100644 index 0000000..68757d7 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/handler/CreateAndUpdateMetaObjectHandler.java @@ -0,0 +1,79 @@ +package com.rzdata.framework.handler; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.rzdata.common.core.domain.BaseEntity; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; + +import java.util.Date; + +/** + * MP注入处理器 + * + * @author Lion Li + * @date 2021/4/25 + */ +@Slf4j +public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) { + BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject(); + Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime()) + ? baseEntity.getCreateTime() : new Date(); + baseEntity.setCreateTime(current); + baseEntity.setUpdateTime(current); + String username = StringUtils.isNotBlank(baseEntity.getCreateBy()) + ? baseEntity.getCreateBy() : getLoginUsername(); + // 当前已登录 且 创建人为空 则填充 + baseEntity.setCreateBy(username); + // 当前已登录 且 更新人为空 则填充 + baseEntity.setUpdateBy(username); + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + @Override + public void updateFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) { + BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject(); + Date current = new Date(); + // 更新时间填充(不管为不为空) + baseEntity.setUpdateTime(current); + String username = getLoginUsername(); + // 当前已登录 更新人填充(不管为不为空) + if (StringUtils.isNotBlank(username)) { + baseEntity.setUpdateBy(username); + } + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 获取登录用户名 + */ + private String getLoginUsername() { + LoginUser loginUser; + try { + loginUser = LoginHelper.getLoginUser(); + } catch (Exception e) { + log.warn("自动注入警告 => 用户未登录"); + return null; + } + return loginUser.getUsername(); + } + +} diff --git a/service/src/main/java/com/rzdata/framework/handler/KeyPrefixHandler.java b/service/src/main/java/com/rzdata/framework/handler/KeyPrefixHandler.java new file mode 100644 index 0000000..91ddb24 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/handler/KeyPrefixHandler.java @@ -0,0 +1,50 @@ +package com.rzdata.framework.handler; + +import com.rzdata.common.utils.StringUtils; +import org.redisson.api.NameMapper; + +/** + * redis缓存key前缀处理 + * + * @author ye + * @date 2022/7/14 17:44 + * @since 4.3.0 + */ +public class KeyPrefixHandler implements NameMapper { + + private final String keyPrefix; + + public KeyPrefixHandler(String keyPrefix) { + //前缀为空 则返回空前缀 + this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":"; + } + + /** + * 增加前缀 + */ + @Override + public String map(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) { + return keyPrefix + name; + } + return name; + } + + /** + * 去除前缀 + */ + @Override + public String unmap(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) { + return name.substring(keyPrefix.length()); + } + return name; + } + +} diff --git a/service/src/main/java/com/rzdata/framework/handler/OpenApiHandler.java b/service/src/main/java/com/rzdata/framework/handler/OpenApiHandler.java new file mode 100644 index 0000000..dcdb5f3 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/handler/OpenApiHandler.java @@ -0,0 +1,272 @@ +package com.rzdata.framework.handler; + +import cn.hutool.core.io.IoUtil; +import io.swagger.v3.core.jackson.TypeNameResolver; +import io.swagger.v3.core.util.AnnotationsUtils; +import io.swagger.v3.oas.annotations.tags.Tags; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.tags.Tag; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springdoc.core.OpenAPIService; +import org.springdoc.core.PropertyResolverUtils; +import org.springdoc.core.SecurityService; +import org.springdoc.core.SpringDocConfigProperties; +import org.springdoc.core.customizers.OpenApiBuilderCustomizer; +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; +import org.springdoc.core.providers.JavadocProvider; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.web.method.HandlerMethod; + +import java.io.StringReader; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 自定义 openapi 处理器 + * 对源码功能进行修改 增强使用 + */ +@SuppressWarnings("all") +public class OpenApiHandler extends OpenAPIService { + + /** + * The constant LOGGER. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIService.class); + + /** + * The Context. + */ + private ApplicationContext context; + + /** + * The Security parser. + */ + private final SecurityService securityParser; + + /** + * The Mappings map. + */ + private final Map mappingsMap = new HashMap<>(); + + /** + * The Springdoc tags. + */ + private final Map springdocTags = new HashMap<>(); + + /** + * The Open api builder customisers. + */ + private final Optional> openApiBuilderCustomisers; + + /** + * The server base URL customisers. + */ + private final Optional> serverBaseUrlCustomizers; + + /** + * The Spring doc config properties. + */ + private final SpringDocConfigProperties springDocConfigProperties; + + /** + * The Open api. + */ + private OpenAPI openAPI; + + /** + * The Cached open api map. + */ + private final Map cachedOpenAPI = new HashMap<>(); + + /** + * The Is servers present. + */ + private boolean isServersPresent; + + /** + * The Server base url. + */ + private String serverBaseUrl; + + /** + * The Property resolver utils. + */ + private final PropertyResolverUtils propertyResolverUtils; + + /** + * The javadoc provider. + */ + private final Optional javadocProvider; + + /** + * The Basic error controller. + */ + private static Class basicErrorController; + + static { + try { + //spring-boot 2 + basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController"); + } catch (ClassNotFoundException e) { + //spring-boot 1 + try { + basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.BasicErrorController"); + } catch (ClassNotFoundException classNotFoundException) { + //Basic error controller class not found + LOGGER.trace(classNotFoundException.getMessage()); + } + } + } + + /** + * Instantiates a new Open api builder. + * + * @param openAPI the open api + * @param securityParser the security parser + * @param springDocConfigProperties the spring doc config properties + * @param propertyResolverUtils the property resolver utils + * @param openApiBuilderCustomizers the open api builder customisers + * @param serverBaseUrlCustomizers the server base url customizers + * @param javadocProvider the javadoc provider + */ + public OpenApiHandler(Optional openAPI, SecurityService securityParser, + SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, + Optional> openApiBuilderCustomizers, + Optional> serverBaseUrlCustomizers, + Optional javadocProvider) { + super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider); + if (openAPI.isPresent()) { + this.openAPI = openAPI.get(); + if (this.openAPI.getComponents() == null) + this.openAPI.setComponents(new Components()); + if (this.openAPI.getPaths() == null) + this.openAPI.setPaths(new Paths()); + if (!CollectionUtils.isEmpty(this.openAPI.getServers())) + this.isServersPresent = true; + } + this.propertyResolverUtils = propertyResolverUtils; + this.securityParser = securityParser; + this.springDocConfigProperties = springDocConfigProperties; + this.openApiBuilderCustomisers = openApiBuilderCustomizers; + this.serverBaseUrlCustomizers = serverBaseUrlCustomizers; + this.javadocProvider = javadocProvider; + if (springDocConfigProperties.isUseFqn()) + TypeNameResolver.std.setUseFqn(true); + } + + @Override + public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) { + + Set tags = new HashSet<>(); + Set tagsStr = new HashSet<>(); + + buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale); + buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale); + + if (!CollectionUtils.isEmpty(tagsStr)) + tagsStr = tagsStr.stream() + .map(str -> propertyResolverUtils.resolve(str, locale)) + .collect(Collectors.toSet()); + + if (springdocTags.containsKey(handlerMethod)) { + io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod); + tagsStr.add(tag.getName()); + if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { + openAPI.addTagsItem(tag); + } + } + + if (!CollectionUtils.isEmpty(tagsStr)) { + if (CollectionUtils.isEmpty(operation.getTags())) + operation.setTags(new ArrayList<>(tagsStr)); + else { + Set operationTagsSet = new HashSet<>(operation.getTags()); + operationTagsSet.addAll(tagsStr); + operation.getTags().clear(); + operation.getTags().addAll(operationTagsSet); + } + } + + if (isAutoTagClasses(operation)) { + + + if (javadocProvider.isPresent()) { + String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType()); + if (StringUtils.isNotBlank(description)) { + io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag(); + + // 自定义部分 修改使用java注释当tag名 + List list = IoUtil.readLines(new StringReader(description), new ArrayList<>()); + // tag.setName(tagAutoName); + tag.setName(list.get(0)); + operation.addTagsItem(list.get(0)); + + tag.setDescription(description); + if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { + openAPI.addTagsItem(tag); + } + } + } else { + String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName()); + operation.addTagsItem(tagAutoName); + } + } + + if (!CollectionUtils.isEmpty(tags)) { + // Existing tags + List openApiTags = openAPI.getTags(); + if (!CollectionUtils.isEmpty(openApiTags)) + tags.addAll(openApiTags); + openAPI.setTags(new ArrayList<>(tags)); + } + + // Handle SecurityRequirement at operation level + io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser + .getSecurityRequirements(handlerMethod); + if (securityRequirements != null) { + if (securityRequirements.length == 0) + operation.setSecurity(Collections.emptyList()); + else + securityParser.buildSecurityRequirement(securityRequirements, operation); + } + + return operation; + } + + private void buildTagsFromMethod(Method method, Set tags, Set tagsStr, Locale locale) { + // method tags + Set tagsSet = AnnotatedElementUtils + .findAllMergedAnnotations(method, Tags.class); + Set methodTags = tagsSet.stream() + .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet()); + methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); + if (!CollectionUtils.isEmpty(methodTags)) { + tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet())); + List allTags = new ArrayList<>(methodTags); + addTags(allTags, tags, locale); + } + } + + private void addTags(List sourceTags, Set tags, Locale locale) { + Optional> optionalTagSet = AnnotationsUtils + .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true); + optionalTagSet.ifPresent(tagsSet -> { + tagsSet.forEach(tag -> { + tag.name(propertyResolverUtils.resolve(tag.getName(), locale)); + tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale)); + if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName()))) + tags.add(tag); + }); + }); + } + +} diff --git a/service/src/main/java/com/rzdata/framework/handler/PlusDataPermissionHandler.java b/service/src/main/java/com/rzdata/framework/handler/PlusDataPermissionHandler.java new file mode 100644 index 0000000..356a3ab --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/handler/PlusDataPermissionHandler.java @@ -0,0 +1,199 @@ +package com.rzdata.framework.handler; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ConcurrentHashSet; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjectUtil; +import com.rzdata.common.annotation.DataColumn; +import com.rzdata.common.annotation.DataPermission; +import com.rzdata.common.core.domain.dto.RoleDTO; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.enums.DataScopeType; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.helper.DataPermissionHelper; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.Parenthesis; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.expression.BeanResolver; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParserContext; +import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 数据权限过滤 + * + * @author Lion Li + * @version 3.5.0 + */ +@Slf4j +public class PlusDataPermissionHandler { + + /** + * 方法或类(名称) 与 注解的映射关系缓存 + */ + private final Map dataPermissionCacheMap = new ConcurrentHashMap<>(); + + /** + * 无效注解方法缓存用于快速返回 + */ + private final Set invalidCacheSet = new ConcurrentHashSet<>(); + + /** + * spel 解析器 + */ + private final ExpressionParser parser = new SpelExpressionParser(); + private final ParserContext parserContext = new TemplateParserContext(); + /** + * bean解析器 用于处理 spel 表达式中对 bean 的调用 + */ + private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); + + + public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { + DataColumn[] dataColumns = findAnnotation(mappedStatementId); + if (ArrayUtil.isEmpty(dataColumns)) { + invalidCacheSet.add(mappedStatementId); + return where; + } + LoginUser currentUser = DataPermissionHelper.getVariable("user"); + if (ObjectUtil.isNull(currentUser)) { + currentUser = LoginHelper.getLoginUser(); + DataPermissionHelper.setVariable("user", currentUser); + } + // 如果是超级管理员,则不过滤数据 + if (LoginHelper.isAdmin()) { + return where; + } + String dataFilterSql = buildDataFilter(dataColumns, isSelect); + if (StringUtils.isBlank(dataFilterSql)) { + return where; + } + try { + Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql); + // 数据权限使用单独的括号 防止与其他条件冲突 + Parenthesis parenthesis = new Parenthesis(expression); + if (ObjectUtil.isNotNull(where)) { + return new AndExpression(where, parenthesis); + } else { + return parenthesis; + } + } catch (JSQLParserException e) { + throw new ServiceException("数据权限解析异常 => " + e.getMessage()); + } + } + + /** + * 构造数据过滤sql + */ + private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) { + // 更新或删除需满足所有条件 + String joinStr = isSelect ? " OR " : " AND "; + LoginUser user = DataPermissionHelper.getVariable("user"); + StandardEvaluationContext context = new StandardEvaluationContext(); + context.setBeanResolver(beanResolver); + DataPermissionHelper.getContext().forEach(context::setVariable); + Set conditions = new HashSet<>(); + for (RoleDTO role : user.getRoles()) { + user.setRoleId(role.getRoleId()); + // 获取角色权限泛型 + DataScopeType type = DataScopeType.findCode(role.getDataScope()); + if (ObjectUtil.isNull(type)) { + throw new ServiceException("角色数据范围异常 => " + role.getDataScope()); + } + // 全部数据权限直接返回 + if (type == DataScopeType.ALL) { + return ""; + } + boolean isSuccess = false; + for (DataColumn dataColumn : dataColumns) { + if (dataColumn.key().length != dataColumn.value().length) { + throw new ServiceException("角色数据范围异常 => key与value长度不匹配"); + } + // 不包含 key 变量 则不处理 + if (!StringUtils.containsAny(type.getSqlTemplate(), + Arrays.stream(dataColumn.key()).map(key -> "#" + key).toArray(String[]::new) + )) { + continue; + } + // 设置注解变量 key 为表达式变量 value 为变量值 + for (int i = 0; i < dataColumn.key().length; i++) { + context.setVariable(dataColumn.key()[i], dataColumn.value()[i]); + } + + // 解析sql模板并填充 + String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class); + conditions.add(joinStr + sql); + isSuccess = true; + } + // 未处理成功则填充兜底方案 + if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) { + conditions.add(joinStr + type.getElseSql()); + } + } + + if (CollUtil.isNotEmpty(conditions)) { + String sql = StreamUtils.join(conditions, Function.identity(), ""); + return sql.substring(joinStr.length()); + } + return ""; + } + + private DataColumn[] findAnnotation(String mappedStatementId) { + StringBuilder sb = new StringBuilder(mappedStatementId); + int index = sb.lastIndexOf("."); + String clazzName = sb.substring(0, index); + String methodName = sb.substring(index + 1, sb.length()); + Class clazz = ClassUtil.loadClass(clazzName); + List methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz)) + .filter(method -> method.getName().equals(methodName)).collect(Collectors.toList()); + DataPermission dataPermission; + // 获取方法注解 + for (Method method : methods) { + dataPermission = dataPermissionCacheMap.get(mappedStatementId); + if (ObjectUtil.isNotNull(dataPermission)) { + return dataPermission.value(); + } + if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) { + dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class); + dataPermissionCacheMap.put(mappedStatementId, dataPermission); + return dataPermission.value(); + } + } + dataPermission = dataPermissionCacheMap.get(clazz.getName()); + if (ObjectUtil.isNotNull(dataPermission)) { + return dataPermission.value(); + } + // 获取类注解 + if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { + dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); + dataPermissionCacheMap.put(clazz.getName(), dataPermission); + return dataPermission.value(); + } + return null; + } + + /** + * 是否为无效方法 无数据权限 + */ + public boolean isInvalid(String mappedStatementId) { + return invalidCacheSet.contains(mappedStatementId); + } +} diff --git a/service/src/main/java/com/rzdata/framework/jackson/BigNumberSerializer.java b/service/src/main/java/com/rzdata/framework/jackson/BigNumberSerializer.java new file mode 100644 index 0000000..388436b --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/jackson/BigNumberSerializer.java @@ -0,0 +1,42 @@ +package com.rzdata.framework.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.ser.std.NumberSerializer; + +import java.io.IOException; + +/** + * 超出 JS 最大最小值 处理 + * + * @author Lion Li + */ +@JacksonStdImpl +public class BigNumberSerializer extends NumberSerializer { + + /** + * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来 + */ + private static final long MAX_SAFE_INTEGER = 9007199254740991L; + private static final long MIN_SAFE_INTEGER = -9007199254740991L; + + /** + * 提供实例 + */ + public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class); + + public BigNumberSerializer(Class rawType) { + super(rawType); + } + + @Override + public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { + // 超出范围 序列化位字符串 + if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { + super.serialize(value, gen, provider); + } else { + gen.writeString(value.toString()); + } + } +} diff --git a/service/src/main/java/com/rzdata/framework/listener/UserActionListener.java b/service/src/main/java/com/rzdata/framework/listener/UserActionListener.java new file mode 100644 index 0000000..1944bbc --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/listener/UserActionListener.java @@ -0,0 +1,133 @@ +package com.rzdata.framework.listener; + +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.listener.SaTokenListener; +import cn.dev33.satoken.stp.SaLoginModel; +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.core.domain.dto.UserOnlineDTO; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.ServletUtils; +import com.rzdata.common.utils.ip.AddressUtils; +import com.rzdata.common.utils.redis.RedisUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + * 用户行为 侦听器的实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Component +@Slf4j +public class UserActionListener implements SaTokenListener { + + private final SaTokenConfig tokenConfig; + + /** + * 每次登录时触发 + */ + @Override + public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { + UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = ServletUtils.getClientIP(); + LoginUser user = LoginHelper.getLoginUser(); + UserOnlineDTO dto = new UserOnlineDTO(); + dto.setIpaddr(ip); + dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + dto.setBrowser(userAgent.getBrowser().getName()); + dto.setOs(userAgent.getOs().getName()); + dto.setLoginTime(System.currentTimeMillis()); + dto.setTokenId(tokenValue); + dto.setUserName(user.getUsername()); + dto.setDeptName(user.getDeptName()); + if(tokenConfig.getTimeout() == -1) { + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); + } else { + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); + } + log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次注销时触发 + */ + @Override + public void doLogout(String loginType, Object loginId, String tokenValue) { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被踢下线时触发 + */ + @Override + public void doKickout(String loginType, Object loginId, String tokenValue) { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + log.info("user doLogoutByLoginId, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被顶下线时触发 + */ + @Override + public void doReplaced(String loginType, Object loginId, String tokenValue) { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被封禁时触发 + */ + @Override + public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { + } + + /** + * 每次被解封时触发 + */ + @Override + public void doUntieDisable(String loginType, Object loginId, String service) { + } + + /** + * 每次打开二级认证时触发 + */ + @Override + public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { + } + + /** + * 每次创建Session时触发 + */ + @Override + public void doCloseSafe(String loginType, String tokenValue, String service) { + } + + /** + * 每次创建Session时触发 + */ + @Override + public void doCreateSession(String id) { + } + + /** + * 每次注销Session时触发 + */ + @Override + public void doLogoutSession(String id) { + } + + /** + * 每次Token续期时触发 + */ + @Override + public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { + } +} diff --git a/service/src/main/java/com/rzdata/framework/manager/PlusSpringCacheManager.java b/service/src/main/java/com/rzdata/framework/manager/PlusSpringCacheManager.java new file mode 100644 index 0000000..61840ab --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/manager/PlusSpringCacheManager.java @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2013-2021 Nikita Koksharov + * + * 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. + */ +package com.rzdata.framework.manager; + +import com.rzdata.common.utils.redis.RedisUtils; +import org.redisson.api.RMap; +import org.redisson.api.RMapCache; +import org.redisson.spring.cache.CacheConfig; +import org.redisson.spring.cache.RedissonCache; +import org.springframework.boot.convert.DurationStyle; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.transaction.TransactionAwareCacheDecorator; +import org.springframework.util.StringUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * A {@link org.springframework.cache.CacheManager} implementation + * backed by Redisson instance. + *

+ * 修改 RedissonSpringCacheManager 源码 + * 重写 cacheName 处理方法 支持多参数 + * + * @author Nikita Koksharov + * + */ +@SuppressWarnings("unchecked") +public class PlusSpringCacheManager implements CacheManager { + + private boolean dynamic = true; + + private boolean allowNullValues = true; + + private boolean transactionAware = true; + + Map configMap = new ConcurrentHashMap<>(); + ConcurrentMap instanceMap = new ConcurrentHashMap<>(); + + /** + * Creates CacheManager supplied by Redisson instance + */ + public PlusSpringCacheManager() { + } + + + /** + * Defines possibility of storing {@code null} values. + *

+ * Default is true + * + * @param allowNullValues stores if true + */ + public void setAllowNullValues(boolean allowNullValues) { + this.allowNullValues = allowNullValues; + } + + /** + * Defines if cache aware of Spring-managed transactions. + * If {@code true} put/evict operations are executed only for successful transaction in after-commit phase. + *

+ * Default is false + * + * @param transactionAware cache is transaction aware if true + */ + public void setTransactionAware(boolean transactionAware) { + this.transactionAware = transactionAware; + } + + /** + * Defines 'fixed' cache names. + * A new cache instance will not be created in dynamic for non-defined names. + *

+ * `null` parameter setups dynamic mode + * + * @param names of caches + */ + public void setCacheNames(Collection names) { + if (names != null) { + for (String name : names) { + getCache(name); + } + dynamic = false; + } else { + dynamic = true; + } + } + + /** + * Set cache config mapped by cache name + * + * @param config object + */ + public void setConfig(Map config) { + this.configMap = (Map) config; + } + + protected CacheConfig createDefaultConfig() { + return new CacheConfig(); + } + + @Override + public Cache getCache(String name) { + Cache cache = instanceMap.get(name); + if (cache != null) { + return cache; + } + if (!dynamic) { + return cache; + } + + CacheConfig config = configMap.get(name); + if (config == null) { + config = createDefaultConfig(); + configMap.put(name, config); + } + + // 重写 cacheName 支持多参数 + String[] array = StringUtils.delimitedListToStringArray(name, "#"); + name = array[0]; + if (array.length > 1) { + config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis()); + } + if (array.length > 2) { + config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis()); + } + if (array.length > 3) { + config.setMaxSize(Integer.parseInt(array[3])); + } + + if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { + return createMap(name, config); + } + + return createMapCache(name, config); + } + + private Cache createMap(String name, CacheConfig config) { + RMap map = RedisUtils.getClient().getMap(name); + + Cache cache = new RedissonCache(map, allowNullValues); + if (transactionAware) { + cache = new TransactionAwareCacheDecorator(cache); + } + Cache oldCache = instanceMap.putIfAbsent(name, cache); + if (oldCache != null) { + cache = oldCache; + } + return cache; + } + + private Cache createMapCache(String name, CacheConfig config) { + RMapCache map = RedisUtils.getClient().getMapCache(name); + + Cache cache = new RedissonCache(map, config, allowNullValues); + if (transactionAware) { + cache = new TransactionAwareCacheDecorator(cache); + } + Cache oldCache = instanceMap.putIfAbsent(name, cache); + if (oldCache != null) { + cache = oldCache; + } else { + map.setMaxSize(config.getMaxSize()); + } + return cache; + } + + @Override + public Collection getCacheNames() { + return Collections.unmodifiableSet(configMap.keySet()); + } + + +} diff --git a/service/src/main/java/com/rzdata/framework/manager/ShutdownManager.java b/service/src/main/java/com/rzdata/framework/manager/ShutdownManager.java new file mode 100644 index 0000000..481c43b --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/manager/ShutdownManager.java @@ -0,0 +1,41 @@ +package com.rzdata.framework.manager; + +import com.rzdata.common.utils.Threads; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.annotation.PreDestroy; +import java.util.concurrent.ScheduledExecutorService; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author Lion Li + */ +@Slf4j +@Component +public class ShutdownManager { + + @Autowired + @Qualifier("scheduledExecutorService") + private ScheduledExecutorService scheduledExecutorService; + + @PreDestroy + public void destroy() { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() { + try { + log.info("====关闭后台任务任务线程池===="); + Threads.shutdownAndAwaitTermination(scheduledExecutorService); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } +} diff --git a/service/src/main/java/com/rzdata/framework/satoken/dao/PlusSaTokenDao.java b/service/src/main/java/com/rzdata/framework/satoken/dao/PlusSaTokenDao.java new file mode 100644 index 0000000..f9310e2 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/satoken/dao/PlusSaTokenDao.java @@ -0,0 +1,178 @@ +package com.rzdata.framework.satoken.dao; + +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.util.SaFoxUtil; +import com.rzdata.common.utils.redis.RedisUtils; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一) + * + * @author Lion Li + */ +@Component +public class PlusSaTokenDao implements SaTokenDao { + + /** + * 获取Value,如无返空 + */ + @Override + public String get(String key) { + return RedisUtils.getCacheObject(key); + } + + /** + * 写入Value,并设定存活时间 (单位: 秒) + */ + @Override + public void set(String key, String value, long timeout) { + if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) { + return; + } + // 判断是否为永不过期 + if (timeout == SaTokenDao.NEVER_EXPIRE) { + RedisUtils.setCacheObject(key, value); + } else { + RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout)); + } + } + + /** + * 修修改指定key-value键值对 (过期时间不变) + */ + @Override + public void update(String key, String value) { + long expire = getTimeout(key); + // -2 = 无此键 + if (expire == SaTokenDao.NOT_VALUE_EXPIRE) { + return; + } + this.set(key, value, expire); + } + + /** + * 删除Value + */ + @Override + public void delete(String key) { + RedisUtils.deleteObject(key); + } + + /** + * 获取Value的剩余存活时间 (单位: 秒) + */ + @Override + public long getTimeout(String key) { + long timeout = RedisUtils.getTimeToLive(key); + return timeout < 0 ? timeout : timeout / 1000; + } + + /** + * 修改Value的剩余存活时间 (单位: 秒) + */ + @Override + public void updateTimeout(String key, long timeout) { + // 判断是否想要设置为永久 + if (timeout == SaTokenDao.NEVER_EXPIRE) { + long expire = getTimeout(key); + if (expire == SaTokenDao.NEVER_EXPIRE) { + // 如果其已经被设置为永久,则不作任何处理 + } else { + // 如果尚未被设置为永久,那么再次set一次 + this.set(key, this.get(key), timeout); + } + return; + } + RedisUtils.expire(key, Duration.ofSeconds(timeout)); + } + + + /** + * 获取Object,如无返空 + */ + @Override + public Object getObject(String key) { + return RedisUtils.getCacheObject(key); + } + + /** + * 写入Object,并设定存活时间 (单位: 秒) + */ + @Override + public void setObject(String key, Object object, long timeout) { + if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) { + return; + } + // 判断是否为永不过期 + if (timeout == SaTokenDao.NEVER_EXPIRE) { + RedisUtils.setCacheObject(key, object); + } else { + RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout)); + } + } + + /** + * 更新Object (过期时间不变) + */ + @Override + public void updateObject(String key, Object object) { + long expire = getObjectTimeout(key); + // -2 = 无此键 + if (expire == SaTokenDao.NOT_VALUE_EXPIRE) { + return; + } + this.setObject(key, object, expire); + } + + /** + * 删除Object + */ + @Override + public void deleteObject(String key) { + RedisUtils.deleteObject(key); + } + + /** + * 获取Object的剩余存活时间 (单位: 秒) + */ + @Override + public long getObjectTimeout(String key) { + long timeout = RedisUtils.getTimeToLive(key); + return timeout < 0 ? timeout : timeout / 1000; + } + + /** + * 修改Object的剩余存活时间 (单位: 秒) + */ + @Override + public void updateObjectTimeout(String key, long timeout) { + // 判断是否想要设置为永久 + if (timeout == SaTokenDao.NEVER_EXPIRE) { + long expire = getObjectTimeout(key); + if (expire == SaTokenDao.NEVER_EXPIRE) { + // 如果其已经被设置为永久,则不作任何处理 + } else { + // 如果尚未被设置为永久,那么再次set一次 + this.setObject(key, this.getObject(key), timeout); + } + return; + } + RedisUtils.expire(key, Duration.ofSeconds(timeout)); + } + + + /** + * 搜索数据 + */ + @Override + public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { + Collection keys = RedisUtils.keys(prefix + "*" + keyword + "*"); + List list = new ArrayList<>(keys); + return SaFoxUtil.searchList(list, start, size, sortType); + } +} diff --git a/service/src/main/java/com/rzdata/framework/satoken/service/SaPermissionImpl.java b/service/src/main/java/com/rzdata/framework/satoken/service/SaPermissionImpl.java new file mode 100644 index 0000000..a20b619 --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/satoken/service/SaPermissionImpl.java @@ -0,0 +1,49 @@ +package com.rzdata.framework.satoken.service; + +import cn.dev33.satoken.stp.StpInterface; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.enums.UserType; +import com.rzdata.common.helper.LoginHelper; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * sa-token 权限管理实现类 + * + * @author Lion Li + */ +@Component +public class SaPermissionImpl implements StpInterface { + + /** + * 获取菜单权限列表 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + LoginUser loginUser = LoginHelper.getLoginUser(); + UserType userType = UserType.getUserType(loginUser.getUserType()); + if (userType == UserType.SYS_USER) { + return new ArrayList<>(loginUser.getMenuPermission()); + } else if (userType == UserType.APP_USER) { + // 其他端 自行根据业务编写 + } + return new ArrayList<>(); + } + + /** + * 获取角色权限列表 + */ + @Override + public List getRoleList(Object loginId, String loginType) { + LoginUser loginUser = LoginHelper.getLoginUser(); + UserType userType = UserType.getUserType(loginUser.getUserType()); + if (userType == UserType.SYS_USER) { + return new ArrayList<>(loginUser.getRolePermission()); + } else if (userType == UserType.APP_USER) { + // 其他端 自行根据业务编写 + } + return new ArrayList<>(); + } +} diff --git a/service/src/main/java/com/rzdata/framework/web/exception/GlobalExceptionHandler.java b/service/src/main/java/com/rzdata/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..3558b0b --- /dev/null +++ b/service/src/main/java/com/rzdata/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,168 @@ +package com.rzdata.framework.web.exception; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.exception.NotPermissionException; +import cn.dev33.satoken.exception.NotRoleException; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.exception.DemoModeException; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.StreamUtils; +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.MyBatisSystemException; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; + +/** + * 全局异常处理器 + * + * @author Lion Li + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 权限码异常 + */ + @ExceptionHandler(NotPermissionException.class) + public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 角色权限异常 + */ + @ExceptionHandler(NotRoleException.class) + public R handleNotRoleException(NotRoleException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 认证失败 + */ + @ExceptionHandler(NotLoginException.class) + public R handleNotLoginException(NotLoginException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public R handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return R.fail(e.getMessage()); + } + + /** + * 主键或UNIQUE索引,数据重复异常 + */ + @ExceptionHandler(DuplicateKeyException.class) + public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage()); + return R.fail("数据库中已存在该记录,请联系管理员确认"); + } + + /** + * Mybatis系统异常 通用处理 + */ + @ExceptionHandler(MyBatisSystemException.class) + public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + String message = e.getMessage(); + if (message.contains("CannotFindDataSourceException")) { + log.error("请求地址'{}', 未找到数据源", requestURI); + return R.fail("未找到数据源,请联系管理员确认"); + } + log.error("请求地址'{}', Mybatis系统异常", requestURI, e); + return R.fail(message); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public R handleServiceException(ServiceException e, HttpServletRequest request) { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage()); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public R handleRuntimeException(RuntimeException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return R.fail(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public R handleException(Exception e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return R.fail(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public R handleBindException(BindException e) { + log.error(e.getMessage(), e); + String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", "); + return R.fail(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(ConstraintViolationException.class) + public R constraintViolationException(ConstraintViolationException e) { + log.error(e.getMessage(), e); + String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", "); + return R.fail(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return R.fail(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public R handleDemoModeException(DemoModeException e) { + return R.fail("演示模式,不允许操作"); + } +} diff --git a/service/src/main/java/com/rzdata/generator/config/GenConfig.java b/service/src/main/java/com/rzdata/generator/config/GenConfig.java new file mode 100644 index 0000000..1da522d --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/config/GenConfig.java @@ -0,0 +1,73 @@ +package com.rzdata.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author rzdata + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = {"classpath:generator.yml"}, encoding = "UTF-8") +public class GenConfig { + + /** + * 作者 + */ + public static String author; + + /** + * 生成包路径 + */ + public static String packageName; + + /** + * 自动去除表前缀,默认是false + */ + public static boolean autoRemovePre; + + /** + * 表前缀(类名不会包含表前缀) + */ + public static String tablePrefix; + + public static String getAuthor() { + return author; + } + + @Value("${author}") + public void setAuthor(String author) { + GenConfig.author = author; + } + + public static String getPackageName() { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/service/src/main/java/com/rzdata/generator/controller/GenController.java b/service/src/main/java/com/rzdata/generator/controller/GenController.java new file mode 100644 index 0000000..844d2a9 --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/controller/GenController.java @@ -0,0 +1,207 @@ +package com.rzdata.generator.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.generator.domain.GenTable; +import com.rzdata.generator.domain.GenTableColumn; +import com.rzdata.generator.service.IGenTableService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 代码生成 操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController { + + private final IGenTableService genTableService; + + /** + * 查询代码生成列表 + */ + @SaCheckPermission("tool:gen:list") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable, PageQuery pageQuery) { + return genTableService.selectPageGenTableList(genTable, pageQuery); + } + + /** + * 修改代码生成业务 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:query") + @GetMapping(value = "/{tableId}") + public R> getInfo(@PathVariable Long tableId) { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return R.ok(map); + } + + /** + * 查询数据库列表 + */ + @SaCheckPermission("tool:gen:list") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable, PageQuery pageQuery) { + return genTableService.selectPageDbTableList(genTable, pageQuery); + } + + /** + * 查询数据表字段列表 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:list") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(Long tableId) { + TableDataInfo dataInfo = new TableDataInfo<>(); + List list = genTableService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + * + * @param tables 表名串 + */ + @SaCheckPermission("tool:gen:import") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public R importTableSave(String tables) { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList); + return R.ok(); + } + + /** + * 修改保存代码生成业务 + */ + @SaCheckPermission("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public R editSave(@Validated @RequestBody GenTable genTable) { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return R.ok(); + } + + /** + * 删除代码生成 + * + * @param tableIds 表ID串 + */ + @SaCheckPermission("tool:gen:remove") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public R remove(@PathVariable Long[] tableIds) { + genTableService.deleteGenTableByIds(tableIds); + return R.ok(); + } + + /** + * 预览代码 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:preview") + @GetMapping("/preview/{tableId}") + public R> preview(@PathVariable("tableId") Long tableId) throws IOException { + Map dataMap = genTableService.previewCode(tableId); + return R.ok(dataMap); + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名 + */ + @SaCheckPermission("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名 + */ + @SaCheckPermission("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public R genCode(@PathVariable("tableName") String tableName) { + genTableService.generatorCode(tableName); + return R.ok(); + } + + /** + * 同步数据库 + * + * @param tableName 表名 + */ + @SaCheckPermission("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public R synchDb(@PathVariable("tableName") String tableName) { + genTableService.synchDb(tableName); + return R.ok(); + } + + /** + * 批量生成代码 + * + * @param tables 表名串 + */ + @SaCheckPermission("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tables) throws IOException { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"rzdata.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IoUtil.write(response.getOutputStream(), false, data); + } +} diff --git a/service/src/main/java/com/rzdata/generator/domain/GenTable.java b/service/src/main/java/com/rzdata/generator/domain/GenTable.java new file mode 100644 index 0000000..6d2574a --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/domain/GenTable.java @@ -0,0 +1,206 @@ +package com.rzdata.generator.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.rzdata.common.constant.GenConstants; +import com.rzdata.common.core.domain.BaseEntity; +import com.rzdata.common.utils.StringUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.ArrayUtils; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * 业务表 gen_table + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("gen_table") +public class GenTable extends BaseEntity { + + /** + * 编号 + */ + @TableId(value = "table_id") + private Long tableId; + + /** + * 表名称 + */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** + * 表描述 + */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** + * 关联父表的表名 + */ + private String subTableName; + + /** + * 本表关联父表的外键名 + */ + private String subTableFkName; + + /** + * 实体类名称(首字母大写) + */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** + * 使用的模板(crud单表操作 tree树表操作 sub主子表操作) + */ + private String tplCategory; + + /** + * 生成包路径 + */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** + * 生成模块名 + */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** + * 生成业务名 + */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** + * 生成功能名 + */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** + * 生成作者 + */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** + * 生成代码方式(0zip压缩包 1自定义路径) + */ + private String genType; + + /** + * 生成路径(不填默认项目路径) + */ + @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) + private String genPath; + + /** + * 主键信息 + */ + @TableField(exist = false) + private GenTableColumn pkColumn; + + /** + * 子表信息 + */ + @TableField(exist = false) + private GenTable subTable; + + /** + * 表列信息 + */ + @Valid + @TableField(exist = false) + private List columns; + + /** + * 其它生成选项 + */ + private String options; + + /** + * 备注 + */ + private String remark; + + /** + * 树编码字段 + */ + @TableField(exist = false) + private String treeCode; + + /** + * 树父编码字段 + */ + @TableField(exist = false) + private String treeParentCode; + + /** + * 树名称字段 + */ + @TableField(exist = false) + private String treeName; + + /* + * 菜单id列表 + */ + @TableField(exist = false) + private List menuIds; + + /** + * 上级菜单ID字段 + */ + @TableField(exist = false) + private String parentMenuId; + + /** + * 上级菜单名称字段 + */ + @TableField(exist = false) + private String parentMenuName; + + public boolean isSub() { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public boolean isTree() { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) { + if (isTree(tplCategory)) { + return StringUtils.equalsAnyIgnoreCase(javaField, + ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); + } + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } +} diff --git a/service/src/main/java/com/rzdata/generator/domain/GenTableColumn.java b/service/src/main/java/com/rzdata/generator/domain/GenTableColumn.java new file mode 100644 index 0000000..f637126 --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/domain/GenTableColumn.java @@ -0,0 +1,223 @@ +package com.rzdata.generator.domain; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.core.domain.BaseEntity; +import com.rzdata.common.utils.StringUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.ibatis.type.JdbcType; + +import javax.validation.constraints.NotBlank; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("gen_table_column") +public class GenTableColumn extends BaseEntity { + + /** + * 编号 + */ + @TableId(value = "column_id") + private Long columnId; + + /** + * 归属表编号 + */ + private Long tableId; + + /** + * 列名称 + */ + private String columnName; + + /** + * 列描述 + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String columnComment; + + /** + * 列类型 + */ + private String columnType; + + /** + * JAVA类型 + */ + private String javaType; + + /** + * JAVA字段名 + */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** + * 是否主键(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isPk; + + /** + * 是否自增(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isIncrement; + + /** + * 是否必填(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isRequired; + + /** + * 是否为插入字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isInsert; + + /** + * 是否编辑字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isEdit; + + /** + * 是否列表字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isList; + + /** + * 是否查询字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) + private String isQuery; + + /** + * 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) + */ + private String queryType; + + /** + * 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) + */ + private String htmlType; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 排序 + */ + private Integer sort; + + public String getCapJavaField() { + return StringUtils.capitalize(javaField); + } + + public boolean isPk() { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) { + return isPk != null && StringUtils.equals("1", isPk); + } + + public boolean isIncrement() { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public boolean isRequired() { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public boolean isInsert() { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public boolean isEdit() { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public boolean isList() { + return isList(this.isList); + } + + public boolean isList(String isList) { + return isList != null && StringUtils.equals("1", isList); + } + + public boolean isQuery() { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public boolean isSuperColumn() { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", + // TreeEntity + "parentName", "parentId"); + } + + public boolean isUsableColumn() { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) { + for (String value : remarks.split(" ")) { + if (StringUtils.isNotEmpty(value)) { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append("").append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } else { + return this.columnComment; + } + } +} diff --git a/service/src/main/java/com/rzdata/generator/mapper/GenTableColumnMapper.java b/service/src/main/java/com/rzdata/generator/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..b662e90 --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/mapper/GenTableColumnMapper.java @@ -0,0 +1,24 @@ +package com.rzdata.generator.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.generator.domain.GenTableColumn; + +import java.util.List; + +/** + * 业务字段 数据层 + * + * @author Lion Li + */ +@InterceptorIgnore(dataPermission = "true") +public interface GenTableColumnMapper extends BaseMapperPlus { + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + List selectDbTableColumnsByName(String tableName); + +} diff --git a/service/src/main/java/com/rzdata/generator/mapper/GenTableMapper.java b/service/src/main/java/com/rzdata/generator/mapper/GenTableMapper.java new file mode 100644 index 0000000..57d7914 --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/mapper/GenTableMapper.java @@ -0,0 +1,58 @@ +package com.rzdata.generator.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.generator.domain.GenTable; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 业务 数据层 + * + * @author Lion Li + */ +@InterceptorIgnore(dataPermission = "true") +public interface GenTableMapper extends BaseMapperPlus { + + /** + * 查询据库列表 + * + * @param genTable 查询条件 + * @return 数据库表集合 + */ + Page selectPageDbTableList(@Param("page") Page page, @Param("genTable") GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + GenTable selectGenTableByName(String tableName); + +} diff --git a/service/src/main/java/com/rzdata/generator/service/GenTableServiceImpl.java b/service/src/main/java/com/rzdata/generator/service/GenTableServiceImpl.java new file mode 100644 index 0000000..690b0be --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/service/GenTableServiceImpl.java @@ -0,0 +1,495 @@ +package com.rzdata.generator.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.constant.Constants; +import com.rzdata.common.constant.GenConstants; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.file.FileUtils; +import com.rzdata.generator.domain.GenTable; +import com.rzdata.generator.domain.GenTableColumn; +import com.rzdata.generator.mapper.GenTableColumnMapper; +import com.rzdata.generator.mapper.GenTableMapper; +import com.rzdata.generator.util.GenUtils; +import com.rzdata.generator.util.VelocityInitializer; +import com.rzdata.generator.util.VelocityUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 业务 服务层实现 + * + * @author Lion Li + */ +@DS("#header.datasource") +@Slf4j +@RequiredArgsConstructor +@Service +public class GenTableServiceImpl implements IGenTableService { + + private final GenTableMapper baseMapper; + private final GenTableColumnMapper genTableColumnMapper; + private final IdentifierGenerator identifierGenerator; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) { + return genTableColumnMapper.selectList(new LambdaQueryWrapper() + .eq(GenTableColumn::getTableId, tableId) + .orderByAsc(GenTableColumn::getSort)); + } + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) { + GenTable genTable = baseMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + @Override + public TableDataInfo selectPageGenTableList(GenTable genTable, PageQuery pageQuery) { + Page page = baseMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable)); + return TableDataInfo.build(page); + } + + private QueryWrapper buildGenTableQueryWrapper(GenTable genTable) { + Map params = genTable.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper.like(StringUtils.isNotBlank(genTable.getTableName()), "lower(table_name)", StringUtils.lowerCase(genTable.getTableName())) + .like(StringUtils.isNotBlank(genTable.getTableComment()), "lower(table_comment)", StringUtils.lowerCase(genTable.getTableComment())) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "create_time", params.get("beginTime"), params.get("endTime")); + return wrapper; + } + + + @Override + public TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery) { + Page page = baseMapper.selectPageDbTableList(pageQuery.build(), genTable); + return TableDataInfo.build(page); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames) { + return baseMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() { + return baseMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void updateGenTable(GenTable genTable) { + String options = JsonUtils.toJsonString(genTable.getParams()); + genTable.setOptions(options); + int row = baseMapper.updateById(genTable); + if (row > 0) { + for (GenTableColumn cenTableColumn : genTable.getColumns()) { + genTableColumnMapper.updateById(cenTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void deleteGenTableByIds(Long[] tableIds) { + List ids = Arrays.asList(tableIds); + baseMapper.deleteBatchIds(ids); + genTableColumnMapper.delete(new LambdaQueryWrapper().in(GenTableColumn::getTableId, ids)); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void importGenTable(List tableList) { + String operName = LoginHelper.getUsername(); + try { + for (GenTable table : tableList) { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = baseMapper.insert(table); + if (row > 0) { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + List saveColumns = new ArrayList<>(); + for (GenTableColumn column : genTableColumns) { + GenUtils.initColumnField(column, table); + saveColumns.add(column); + } + if (CollUtil.isNotEmpty(saveColumns)) { + genTableColumnMapper.insertBatch(saveColumns); + } + } + } + } catch (Exception e) { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = baseMapper.selectGenTableById(tableId); + List menuIds = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + menuIds.add(identifierGenerator.nextId(null).longValue()); + } + table.setMenuIds(menuIds); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(String tableName) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableName, zip); + IoUtil.close(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) { + // 查询表信息 + GenTable table = baseMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + String path = getGenPath(table, template); + FileUtils.writeUtf8String(sw.toString(), path); + } catch (Exception e) { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void synchDb(String tableName) { + GenTable table = baseMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = StreamUtils.toIdentityMap(tableColumns, GenTableColumn::getColumnName); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (CollUtil.isEmpty(dbTableColumns)) { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = StreamUtils.toList(dbTableColumns, GenTableColumn::getColumnName); + + List saveColumns = new ArrayList<>(); + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + genTableColumnMapper.updateById(column); + } else { + genTableColumnMapper.insert(column); + } + }); + if (CollUtil.isNotEmpty(saveColumns)) { + genTableColumnMapper.insertBatch(saveColumns); + } + List delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName())); + if (CollUtil.isNotEmpty(delColumns)) { + List ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId); + genTableColumnMapper.deleteBatchIds(ids); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableNames) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableName : tableNames) { + generatorCode(tableName, zip); + } + IoUtil.close(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip) { + // 查询表信息 + GenTable table = baseMapper.selectGenTableByName(tableName); + List menuIds = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + menuIds.add(identifierGenerator.nextId(null).longValue()); + } + table.setMenuIds(menuIds); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString()); + IoUtil.close(sw); + zip.flush(); + zip.closeEntry(); + } catch (IOException e) { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) { + String options = JsonUtils.toJsonString(genTable.getParams()); + Dict paramsObj = JsonUtils.parseMap(options); + if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_CODE))) { + throw new ServiceException("树编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_PARENT_CODE))) { + throw new ServiceException("树父编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_NAME))) { + throw new ServiceException("树名称字段不能为空"); + } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) { + if (StringUtils.isEmpty(genTable.getSubTableName())) { + throw new ServiceException("关联子表的表名不能为空"); + } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) { + throw new ServiceException("子表关联的外键名不能为空"); + } + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) { + for (GenTableColumn column : table.getColumns()) { + if (column.isPk()) { + table.setPkColumn(column); + break; + } + } + if (ObjectUtil.isNull(table.getPkColumn())) { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) { + for (GenTableColumn column : table.getSubTable().getColumns()) { + if (column.isPk()) { + table.getSubTable().setPkColumn(column); + break; + } + } + if (ObjectUtil.isNull(table.getSubTable().getPkColumn())) { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) { + table.setSubTable(baseMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) { + Dict paramsObj = JsonUtils.parseMap(genTable.getOptions()); + if (ObjectUtil.isNotNull(paramsObj)) { + String treeCode = paramsObj.getStr(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getStr(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getStr(GenConstants.TREE_NAME); + String parentMenuId = paramsObj.getStr(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getStr(GenConstants.PARENT_MENU_NAME); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } +} + diff --git a/service/src/main/java/com/rzdata/generator/service/IGenTableService.java b/service/src/main/java/com/rzdata/generator/service/IGenTableService.java new file mode 100644 index 0000000..1b348e4 --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/service/IGenTableService.java @@ -0,0 +1,133 @@ +package com.rzdata.generator.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.generator.domain.GenTable; +import com.rzdata.generator.domain.GenTableColumn; + +import java.util.List; +import java.util.Map; + +/** + * 业务 服务层 + * + * @author Lion Li + */ +public interface IGenTableService { + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + List selectGenTableColumnListByTableId(Long tableId); + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + TableDataInfo selectPageGenTableList(GenTable genTable, PageQuery pageQuery); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + void deleteGenTableByIds(Long[] tableIds); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + void importGenTable(List tableList); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + void synchDb(String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + void validateEdit(GenTable genTable); +} diff --git a/service/src/main/java/com/rzdata/generator/util/GenUtils.java b/service/src/main/java/com/rzdata/generator/util/GenUtils.java new file mode 100644 index 0000000..a0a9c5b --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/util/GenUtils.java @@ -0,0 +1,232 @@ +package com.rzdata.generator.util; + +import com.rzdata.common.constant.GenConstants; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.generator.config.GenConfig; +import com.rzdata.generator.domain.GenTable; +import com.rzdata.generator.domain.GenTableColumn; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.RegExUtils; + +import java.util.Arrays; + +/** + * 代码生成器 工具类 + * + * @author rzdata + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class GenUtils { + + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable, String operName) { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // BO对象 默认插入勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) { + column.setIsInsert(GenConstants.REQUIRE); + } + // BO对象 默认编辑勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) { + column.setIsEdit(GenConstants.REQUIRE); + } + // BO对象 默认是否必填勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) { + column.setIsRequired(GenConstants.REQUIRE); + } + // VO对象 默认返回勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName)) { + column.setIsList(GenConstants.REQUIRE); + } + // BO对象 默认查询勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) { + int firstIndex = tableName.indexOf("_"); + int nameLength = tableName.length(); + String businessName = StringUtils.substring(tableName, firstIndex + 1, nameLength); + businessName = StringUtils.toCamelCase(businessName); + return businessName; + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) { + String[] searchList = StringUtils.split(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * @return + */ + public static String replaceFirst(String replacementm, String[] searchList) { + String text = replacementm; + for (String searchString : searchList) { + if (replacementm.startsWith(searchString)) { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) { + if (StringUtils.indexOf(columnType, '(') > 0) { + return StringUtils.substringBefore(columnType, "("); + } else { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) { + if (StringUtils.indexOf(columnType, '(') > 0) { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } else { + return 0; + } + } +} diff --git a/service/src/main/java/com/rzdata/generator/util/VelocityInitializer.java b/service/src/main/java/com/rzdata/generator/util/VelocityInitializer.java new file mode 100644 index 0000000..e57748e --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/util/VelocityInitializer.java @@ -0,0 +1,35 @@ +package com.rzdata.generator.util; + +import com.rzdata.common.constant.Constants; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.velocity.app.Velocity; + +import java.util.Properties; + +/** + * VelocityEngine工厂 + * + * @author rzdata + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class VelocityInitializer { + + /** + * 初始化vm方法 + */ + public static void initVelocity() { + Properties p = new Properties(); + try { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/service/src/main/java/com/rzdata/generator/util/VelocityUtils.java b/service/src/main/java/com/rzdata/generator/util/VelocityUtils.java new file mode 100644 index 0000000..d1f49f2 --- /dev/null +++ b/service/src/main/java/com/rzdata/generator/util/VelocityUtils.java @@ -0,0 +1,369 @@ +package com.rzdata.generator.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ObjectUtil; +import com.rzdata.common.constant.GenConstants; +import com.rzdata.common.helper.DataBaseHelper; +import com.rzdata.common.utils.DateUtils; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.generator.domain.GenTable; +import com.rzdata.generator.domain.GenTableColumn; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.velocity.VelocityContext; + +import java.util.*; + +/** + * 模板处理工具类 + * + * @author rzdata + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class VelocityUtils { + + /** + * 项目空间路径 + */ + private static final String PROJECT_PATH = "main/java"; + + /** + * mybatis空间路径 + */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** + * 默认上级菜单,系统工具 + */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) { + context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory) { + List templates = new ArrayList(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/vo.java.vm"); + templates.add("vm/java/bo.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + if (DataBaseHelper.isOracle()) { + templates.add("vm/sql/oracle/sql.vm"); + } else if (DataBaseHelper.isPostgerSql()) { + templates.add("vm/sql/postgres/sql.vm"); + } else if (DataBaseHelper.isSqlServer()) { + templates.add("vm/sql/sqlserver/sql.vm"); + } else { + templates.add("vm/sql/sql.vm"); + } + templates.add("vm/js/api.js.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) { + templates.add("vm/vue/index.vue.vm"); + } else if (GenConstants.TPL_TREE.equals(tplCategory)) { + templates.add("vm/vue/index-tree.vue.vm"); + } else if (GenConstants.TPL_SUB.equals(tplCategory)) { + templates.add("vm/vue/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("vo.java.vm")) { + fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className); + } + if (template.contains("bo.java.vm")) { + fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } else if (template.contains("mapper.java.vm")) { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } else if (template.contains("service.java.vm")) { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } else if (template.contains("serviceImpl.java.vm")) { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } else if (template.contains("controller.java.vm")) { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } else if (template.contains("mapper.xml.vm")) { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } else if (template.contains("sql.vm")) { + fileName = businessName + "Menu.sql"; + } else if (template.contains("api.js.vm")) { + fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } else if (template.contains("index.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } else if (template.contains("index-tree.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + if (ObjectUtil.isNotNull(subGenTable)) { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) { + List columns = genTable.getColumns(); + Set dicts = new HashSet(); + addDicts(dicts, columns); + if (ObjectUtil.isNotNull(genTable.getSubTable())) { + List subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set dicts, List columns) { + for (GenTableColumn column : columns) { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) { + return paramsObj.getStr(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(Map paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) { + return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE))); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) { + return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String treeName = paramsObj.getStr(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) { + if (column.isList()) { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) { + break; + } + } + } + return num; + } +} diff --git a/service/src/main/java/com/rzdata/job/config/XxlJobConfig.java b/service/src/main/java/com/rzdata/job/config/XxlJobConfig.java new file mode 100644 index 0000000..bcc2324 --- /dev/null +++ b/service/src/main/java/com/rzdata/job/config/XxlJobConfig.java @@ -0,0 +1,42 @@ +package com.rzdata.job.config; + +import com.rzdata.job.config.properties.XxlJobProperties; +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * xxl-job config + * + * @author Lion Li + */ +@Slf4j +@Configuration +@EnableConfigurationProperties(XxlJobProperties.class) +@AllArgsConstructor +@ConditionalOnProperty(prefix = "xxl.job", name = "enabled", havingValue = "true") +public class XxlJobConfig { + + private final XxlJobProperties xxlJobProperties; + + @Bean + public XxlJobSpringExecutor xxlJobExecutor() { + log.info(">>>>>>>>>>> xxl-job config init."); + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses()); + xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken()); + XxlJobProperties.Executor executor = xxlJobProperties.getExecutor(); + xxlJobSpringExecutor.setAppname(executor.getAppname()); + xxlJobSpringExecutor.setAddress(executor.getAddress()); + xxlJobSpringExecutor.setIp(executor.getIp()); + xxlJobSpringExecutor.setPort(executor.getPort()); + xxlJobSpringExecutor.setLogPath(executor.getLogPath()); + xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays()); + return xxlJobSpringExecutor; + } + +} diff --git a/service/src/main/java/com/rzdata/job/config/properties/XxlJobProperties.java b/service/src/main/java/com/rzdata/job/config/properties/XxlJobProperties.java new file mode 100644 index 0000000..500e263 --- /dev/null +++ b/service/src/main/java/com/rzdata/job/config/properties/XxlJobProperties.java @@ -0,0 +1,40 @@ +package com.rzdata.job.config.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * xxljob配置类 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "xxl.job") +public class XxlJobProperties { + + private Boolean enabled; + + private String adminAddresses; + + private String accessToken; + + private Executor executor; + + @Data + @NoArgsConstructor + public static class Executor { + + private String appname; + + private String address; + + private String ip; + + private int port; + + private String logPath; + + private int logRetentionDays; + } +} diff --git a/service/src/main/java/com/rzdata/job/service/SampleService.java b/service/src/main/java/com/rzdata/job/service/SampleService.java new file mode 100644 index 0000000..aedc2a8 --- /dev/null +++ b/service/src/main/java/com/rzdata/job/service/SampleService.java @@ -0,0 +1,252 @@ +package com.rzdata.job.service; + +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; + +/** + * XxlJob开发示例(Bean模式) + *

+ * 开发步骤: + * 1、任务开发:在Spring Bean实例中,开发Job方法; + * 2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。 + * 3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志; + * 4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果; + * + * @author xuxueli 2019-12-11 21:52:51 + */ +@Slf4j +@Service +public class SampleService { + + + /** + * 1、简单任务示例(Bean模式) + */ + @XxlJob("demoJobHandler") + public void demoJobHandler() throws Exception { + XxlJobHelper.log("XXL-JOB, Hello World."); + + for (int i = 0; i < 5; i++) { + XxlJobHelper.log("beat at:" + i); + } + // default success + } + + + /** + * 2、分片广播任务 + */ + @XxlJob("shardingJobHandler") + public void shardingJobHandler() throws Exception { + + // 分片参数 + int shardIndex = XxlJobHelper.getShardIndex(); + int shardTotal = XxlJobHelper.getShardTotal(); + + XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); + + // 业务逻辑 + for (int i = 0; i < shardTotal; i++) { + if (i == shardIndex) { + XxlJobHelper.log("第 {} 片, 命中分片开始处理", i); + } else { + XxlJobHelper.log("第 {} 片, 忽略", i); + } + } + + } + + + /** + * 3、命令行任务 + */ + @XxlJob("commandJobHandler") + public void commandJobHandler() throws Exception { + String command = XxlJobHelper.getJobParam(); + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // command process + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.command(command); + processBuilder.redirectErrorStream(true); + + Process process = processBuilder.start(); + //Process process = Runtime.getRuntime().exec(command); + + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobHelper.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobHelper.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + // default success + } else { + XxlJobHelper.handleFail("command exit value(" + exitValue + ") is failed"); + } + + } + + + /** + * 4、跨平台Http任务 + * 参数示例: + * "url: http://www.baidu.com\n" + + * "method: get\n" + + * "data: content\n"; + */ + @XxlJob("httpJobHandler") + public void httpJobHandler() throws Exception { + + // param parse + String param = XxlJobHelper.getJobParam(); + if (param == null || param.trim().length() == 0) { + XxlJobHelper.log("param[" + param + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + + String[] httpParams = param.split("\n"); + String url = null; + String method = null; + String data = null; + for (String httpParam : httpParams) { + if (httpParam.startsWith("url:")) { + url = httpParam.substring(httpParam.indexOf("url:") + 4).trim(); + } + if (httpParam.startsWith("method:")) { + method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase(); + } + if (httpParam.startsWith("data:")) { + data = httpParam.substring(httpParam.indexOf("data:") + 5).trim(); + } + } + + // param valid + if (url == null || url.trim().length() == 0) { + XxlJobHelper.log("url[" + url + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + if (method == null || !Arrays.asList("GET", "POST").contains(method)) { + XxlJobHelper.log("method[" + method + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + boolean isPostMethod = method.equals("POST"); + + // request + HttpURLConnection connection = null; + BufferedReader bufferedReader = null; + try { + // connection + URL realUrl = new URL(url); + connection = (HttpURLConnection) realUrl.openConnection(); + + // connection setting + connection.setRequestMethod(method); + connection.setDoOutput(isPostMethod); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setReadTimeout(5 * 1000); + connection.setConnectTimeout(3 * 1000); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8"); + + // do connection + connection.connect(); + + // data + if (isPostMethod && data != null && data.trim().length() > 0) { + DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); + dataOutputStream.write(data.getBytes("UTF-8")); + dataOutputStream.flush(); + dataOutputStream.close(); + } + + // valid StatusCode + int statusCode = connection.getResponseCode(); + if (statusCode != 200) { + throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid."); + } + + // result + bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); + StringBuilder result = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String responseMsg = result.toString(); + + XxlJobHelper.log(responseMsg); + + return; + } catch (Exception e) { + XxlJobHelper.log(e); + + XxlJobHelper.handleFail(); + return; + } finally { + try { + if (bufferedReader != null) { + bufferedReader.close(); + } + if (connection != null) { + connection.disconnect(); + } + } catch (Exception e2) { + XxlJobHelper.log(e2); + } + } + + } + + /** + * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑; + */ + @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy") + public void demoJobHandler2() throws Exception { + XxlJobHelper.log("XXL-JOB, Hello World."); + } + + public void init() { + log.info("init"); + } + + public void destroy() { + log.info("destory"); + } + + +} diff --git a/service/src/main/java/com/rzdata/sms/config/SmsConfig.java b/service/src/main/java/com/rzdata/sms/config/SmsConfig.java new file mode 100644 index 0000000..5b05ab5 --- /dev/null +++ b/service/src/main/java/com/rzdata/sms/config/SmsConfig.java @@ -0,0 +1,44 @@ +package com.rzdata.sms.config; + +import com.rzdata.sms.config.properties.SmsProperties; +import com.rzdata.sms.core.SmsTemplate; +import com.rzdata.sms.core.TencentSmsTemplate; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 短信配置类 + * + * @author Lion Li + * @version 4.2.0 + */ +@Configuration +public class SmsConfig { + +// @Configuration +// @ConditionalOnProperty(value = "sms.enabled", havingValue = "true") +// @ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class) +// static class AliyunSmsConfig { +// +// @Bean +// public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) { +// return new AliyunSmsTemplate(smsProperties); +// } +// +// } + + @Configuration + @ConditionalOnProperty(value = "sms.enabled", havingValue = "true") + @ConditionalOnClass(com.tencentcloudapi.sms.v20190711.SmsClient.class) + static class TencentSmsConfig { + + @Bean + public SmsTemplate tencentSmsTemplate(SmsProperties smsProperties) { + return new TencentSmsTemplate(smsProperties); + } + + } + +} diff --git a/service/src/main/java/com/rzdata/sms/config/properties/SmsProperties.java b/service/src/main/java/com/rzdata/sms/config/properties/SmsProperties.java new file mode 100644 index 0000000..810fcf6 --- /dev/null +++ b/service/src/main/java/com/rzdata/sms/config/properties/SmsProperties.java @@ -0,0 +1,47 @@ +package com.rzdata.sms.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * SMS短信 配置属性 + * + * @author Lion Li + * @version 4.2.0 + */ +@Data +@Component +@ConfigurationProperties(prefix = "sms") +public class SmsProperties { + + private Boolean enabled; + + /** + * 配置节点 + * 阿里云 dysmsapi.aliyuncs.com + * 腾讯云 sms.tencentcloudapi.com + */ + private String endpoint; + + /** + * key + */ + private String accessKeyId; + + /** + * 密匙 + */ + private String accessKeySecret; + + /* + * 短信签名 + */ + private String signName; + + /** + * 短信应用ID (腾讯专属) + */ + private String sdkAppId; + +} diff --git a/service/src/main/java/com/rzdata/sms/core/AliyunSmsTemplate.java b/service/src/main/java/com/rzdata/sms/core/AliyunSmsTemplate.java new file mode 100644 index 0000000..63e5850 --- /dev/null +++ b/service/src/main/java/com/rzdata/sms/core/AliyunSmsTemplate.java @@ -0,0 +1,66 @@ +package com.rzdata.sms.core; + +import com.aliyun.dysmsapi20170525.Client; +import com.aliyun.dysmsapi20170525.models.SendSmsRequest; +import com.aliyun.dysmsapi20170525.models.SendSmsResponse; +import com.aliyun.teaopenapi.models.Config; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.sms.config.properties.SmsProperties; +import com.rzdata.sms.entity.SmsResult; +import com.rzdata.sms.exception.SmsException; +import lombok.SneakyThrows; + +import java.util.Map; + +/** + * Aliyun 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public class AliyunSmsTemplate implements SmsTemplate { + + private SmsProperties properties; + + private Client client; + + @SneakyThrows(Exception.class) + public AliyunSmsTemplate(SmsProperties smsProperties) { + this.properties = smsProperties; + Config config = new Config() + // 您的AccessKey ID + .setAccessKeyId(smsProperties.getAccessKeyId()) + // 您的AccessKey Secret + .setAccessKeySecret(smsProperties.getAccessKeySecret()) + // 访问的域名 + .setEndpoint(smsProperties.getEndpoint()); + this.client = new Client(config); + } + + @Override + public SmsResult send(String phones, String templateId, Map param) { + if (StringUtils.isBlank(phones)) { + throw new SmsException("手机号不能为空"); + } + if (StringUtils.isBlank(templateId)) { + throw new SmsException("模板ID不能为空"); + } + SendSmsRequest req = new SendSmsRequest() + .setPhoneNumbers(phones) + .setSignName(properties.getSignName()) + .setTemplateCode(templateId) + .setTemplateParam(JsonUtils.toJsonString(param)); + try { + SendSmsResponse resp = client.sendSms(req); + return SmsResult.builder() + .isSuccess("OK".equals(resp.getBody().getCode())) + .message(resp.getBody().getMessage()) + .response(JsonUtils.toJsonString(resp)) + .build(); + } catch (Exception e) { + throw new SmsException(e.getMessage()); + } + } + +} diff --git a/service/src/main/java/com/rzdata/sms/core/SmsTemplate.java b/service/src/main/java/com/rzdata/sms/core/SmsTemplate.java new file mode 100644 index 0000000..52dc50c --- /dev/null +++ b/service/src/main/java/com/rzdata/sms/core/SmsTemplate.java @@ -0,0 +1,26 @@ +package com.rzdata.sms.core; + +import com.rzdata.sms.entity.SmsResult; + +import java.util.Map; + +/** + * 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public interface SmsTemplate { + + /** + * 发送短信 + * + * @param phones 电话号(多个逗号分割) + * @param templateId 模板id + * @param param 模板对应参数 + * 阿里 需使用 模板变量名称对应内容 例如: code=1234 + * 腾讯 需使用 模板变量顺序对应内容 例如: 1=1234, 1为模板内第一个参数 + */ + SmsResult send(String phones, String templateId, Map param); + +} diff --git a/service/src/main/java/com/rzdata/sms/core/TencentSmsTemplate.java b/service/src/main/java/com/rzdata/sms/core/TencentSmsTemplate.java new file mode 100644 index 0000000..bbe0eb3 --- /dev/null +++ b/service/src/main/java/com/rzdata/sms/core/TencentSmsTemplate.java @@ -0,0 +1,82 @@ +package com.rzdata.sms.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.sms.config.properties.SmsProperties; +import com.rzdata.sms.entity.SmsResult; +import com.rzdata.sms.exception.SmsException; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20190711.SmsClient; +import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse; +import com.tencentcloudapi.sms.v20190711.models.SendStatus; +import lombok.SneakyThrows; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Tencent 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public class TencentSmsTemplate implements SmsTemplate { + + private SmsProperties properties; + + private SmsClient client; + + @SneakyThrows(Exception.class) + public TencentSmsTemplate(SmsProperties smsProperties) { + this.properties = smsProperties; + Credential credential = new Credential(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret()); + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint(smsProperties.getEndpoint()); + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + this.client = new SmsClient(credential, "", clientProfile); + } + + @Override + public SmsResult send(String phones, String templateId, Map param) { + if (StringUtils.isBlank(phones)) { + throw new SmsException("手机号不能为空"); + } + if (StringUtils.isBlank(templateId)) { + throw new SmsException("模板ID不能为空"); + } + SendSmsRequest req = new SendSmsRequest(); + Set set = Arrays.stream(phones.split(",")).map(p -> "+86" + p).collect(Collectors.toSet()); + req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class)); + if (CollUtil.isNotEmpty(param)) { + req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class)); + } + req.setTemplateID(templateId); + req.setSign(properties.getSignName()); + req.setSmsSdkAppid(properties.getSdkAppId()); + try { + SendSmsResponse resp = client.SendSms(req); + SmsResult.SmsResultBuilder builder = SmsResult.builder() + .isSuccess(true) + .message("send success") + .response(JsonUtils.toJsonString(resp)); + for (SendStatus sendStatus : resp.getSendStatusSet()) { + if (!"Ok".equals(sendStatus.getCode())) { + builder.isSuccess(false).message(sendStatus.getMessage()); + break; + } + } + return builder.build(); + } catch (Exception e) { + throw new SmsException(e.getMessage()); + } + } + +} diff --git a/service/src/main/java/com/rzdata/sms/entity/SmsResult.java b/service/src/main/java/com/rzdata/sms/entity/SmsResult.java new file mode 100644 index 0000000..af379ab --- /dev/null +++ b/service/src/main/java/com/rzdata/sms/entity/SmsResult.java @@ -0,0 +1,31 @@ +package com.rzdata.sms.entity; + +import lombok.Builder; +import lombok.Data; + +/** + * 上传返回体 + * + * @author Lion Li + */ +@Data +@Builder +public class SmsResult { + + /** + * 是否成功 + */ + private boolean isSuccess; + + /** + * 响应消息 + */ + private String message; + + /** + * 实际响应体 + *

+ * 可自行转换为 SDK 对应的 SendSmsResponse + */ + private String response; +} diff --git a/service/src/main/java/com/rzdata/sms/exception/SmsException.java b/service/src/main/java/com/rzdata/sms/exception/SmsException.java new file mode 100644 index 0000000..48f51f0 --- /dev/null +++ b/service/src/main/java/com/rzdata/sms/exception/SmsException.java @@ -0,0 +1,16 @@ +package com.rzdata.sms.exception; + +/** + * Sms异常类 + * + * @author Lion Li + */ +public class SmsException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public SmsException(String msg) { + super(msg); + } + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysCache.java b/service/src/main/java/com/rzdata/system/domain/SysCache.java new file mode 100644 index 0000000..a63d996 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysCache.java @@ -0,0 +1,47 @@ +package com.rzdata.system.domain; + +import com.rzdata.common.utils.StringUtils; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 缓存信息 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class SysCache { + + /** + * 缓存名称 + */ + private String cacheName = ""; + + /** + * 缓存键名 + */ + private String cacheKey = ""; + + /** + * 缓存内容 + */ + private String cacheValue = ""; + + /** + * 备注 + */ + private String remark = ""; + + public SysCache(String cacheName, String remark) { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysConfig.java b/service/src/main/java/com/rzdata/system/domain/SysConfig.java new file mode 100644 index 0000000..025a4d1 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysConfig.java @@ -0,0 +1,71 @@ +package com.rzdata.system.domain; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.convert.ExcelDictConvert; +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +/** + * 参数配置表 sys_config + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_config") +@ExcelIgnoreUnannotated +public class SysConfig extends BaseEntity { + + /** + * 参数主键 + */ + @ExcelProperty(value = "参数主键") + @TableId(value = "config_id") + private Long configId; + + /** + * 参数名称 + */ + @ExcelProperty(value = "参数名称") + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + private String configName; + + /** + * 参数键名 + */ + @ExcelProperty(value = "参数键名") + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + private String configKey; + + /** + * 参数键值 + */ + @ExcelProperty(value = "参数键值") + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + @ExcelProperty(value = "系统内置", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private String configType; + + /** + * 备注 + */ + private String remark; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysLogininfor.java b/service/src/main/java/com/rzdata/system/domain/SysLogininfor.java new file mode 100644 index 0000000..242de06 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysLogininfor.java @@ -0,0 +1,91 @@ +package com.rzdata.system.domain; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.convert.ExcelDictConvert; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 系统访问记录表 sys_logininfor + * + * @author Lion Li + */ + +@Data +@TableName("sys_logininfor") +@ExcelIgnoreUnannotated +public class SysLogininfor implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @ExcelProperty(value = "序号") + @TableId(value = "info_id") + private Long infoId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "用户账号") + private String userName; + + /** + * 登录状态 0成功 1失败 + */ + @ExcelProperty(value = "登录状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_common_status") + private String status; + + /** + * 登录IP地址 + */ + @ExcelProperty(value = "登录地址") + private String ipaddr; + + /** + * 登录地点 + */ + @ExcelProperty(value = "登录地点") + private String loginLocation; + + /** + * 浏览器类型 + */ + @ExcelProperty(value = "浏览器") + private String browser; + + /** + * 操作系统 + */ + @ExcelProperty(value = "操作系统") + private String os; + + /** + * 提示消息 + */ + @ExcelProperty(value = "提示消息") + private String msg; + + /** + * 访问时间 + */ + @ExcelProperty(value = "访问时间") + private Date loginTime; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysNotice.java b/service/src/main/java/com/rzdata/system/domain/SysNotice.java new file mode 100644 index 0000000..317b12d --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysNotice.java @@ -0,0 +1,58 @@ +package com.rzdata.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.core.domain.BaseEntity; +import com.rzdata.common.xss.Xss; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + + +/** + * 通知公告表 sys_notice + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_notice") +public class SysNotice extends BaseEntity { + + /** + * 公告ID + */ + @TableId(value = "notice_id") + private Long noticeId; + + /** + * 公告标题 + */ + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + private String noticeTitle; + + /** + * 公告类型(1通知 2公告) + */ + private String noticeType; + + /** + * 公告内容 + */ + private String noticeContent; + + /** + * 公告状态(0正常 1关闭) + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysOperLog.java b/service/src/main/java/com/rzdata/system/domain/SysOperLog.java new file mode 100644 index 0000000..faf0000 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysOperLog.java @@ -0,0 +1,142 @@ +package com.rzdata.system.domain; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.convert.ExcelDictConvert; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 操作日志记录表 oper_log + * + * @author Lion Li + */ + +@Data +@TableName("sys_oper_log") +@ExcelIgnoreUnannotated +public class SysOperLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @ExcelProperty(value = "日志主键") + @TableId(value = "oper_id") + private Long operId; + + /** + * 操作模块 + */ + @ExcelProperty(value = "操作模块") + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + @ExcelProperty(value = "业务类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_oper_type") + private Integer businessType; + + /** + * 业务类型数组 + */ + @TableField(exist = false) + private Integer[] businessTypes; + + /** + * 请求方法 + */ + @ExcelProperty(value = "请求方法") + private String method; + + /** + * 请求方式 + */ + @ExcelProperty(value = "请求方式") + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + @ExcelProperty(value = "操作类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** + * 操作人员 + */ + @ExcelProperty(value = "操作人员") + private String operName; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 请求url + */ + @ExcelProperty(value = "请求地址") + private String operUrl; + + /** + * 操作地址 + */ + @ExcelProperty(value = "操作地址") + private String operIp; + + /** + * 操作地点 + */ + @ExcelProperty(value = "操作地点") + private String operLocation; + + /** + * 请求参数 + */ + @ExcelProperty(value = "请求参数") + private String operParam; + + /** + * 返回参数 + */ + @ExcelProperty(value = "返回参数") + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_common_status") + private Integer status; + + /** + * 错误消息 + */ + @ExcelProperty(value = "错误消息") + private String errorMsg; + + /** + * 操作时间 + */ + @ExcelProperty(value = "操作时间") + private Date operTime; + + /** + * 请求参数 + */ + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysOss.java b/service/src/main/java/com/rzdata/system/domain/SysOss.java new file mode 100644 index 0000000..0ee626d --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysOss.java @@ -0,0 +1,50 @@ +package com.rzdata.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * OSS对象存储对象 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_oss") +public class SysOss extends BaseEntity { + + /** + * 对象存储主键 + */ + @TableId(value = "oss_id") + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + + /** + * 服务商 + */ + private String service; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysOssConfig.java b/service/src/main/java/com/rzdata/system/domain/SysOssConfig.java new file mode 100644 index 0000000..0fffaa7 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysOssConfig.java @@ -0,0 +1,89 @@ +package com.rzdata.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 对象存储配置对象 sys_oss_config + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_oss_config") +public class SysOssConfig extends BaseEntity { + + /** + * 主建 + */ + @TableId(value = "oss_config_id") + private Long ossConfigId; + + /** + * 配置key + */ + private String configKey; + + /** + * accessKey + */ + private String accessKey; + + /** + * 秘钥 + */ + private String secretKey; + + /** + * 桶名称 + */ + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(0否 1是) + */ + private String isHttps; + + /** + * 域 + */ + private String region; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + private String accessPolicy; +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysPost.java b/service/src/main/java/com/rzdata/system/domain/SysPost.java new file mode 100644 index 0000000..aa18654 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysPost.java @@ -0,0 +1,78 @@ +package com.rzdata.system.domain; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.convert.ExcelDictConvert; +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * 岗位表 sys_post + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_post") +@ExcelIgnoreUnannotated +public class SysPost extends BaseEntity { + + /** + * 岗位序号 + */ + @ExcelProperty(value = "岗位序号") + @TableId(value = "post_id") + private Long postId; + + /** + * 岗位编码 + */ + @ExcelProperty(value = "岗位编码") + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + private String postCode; + + /** + * 岗位名称 + */ + @ExcelProperty(value = "岗位名称") + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + private String postName; + + /** + * 岗位排序 + */ + @ExcelProperty(value = "岗位排序") + @NotNull(message = "显示顺序不能为空") + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 用户是否存在此岗位标识 默认不存在 + */ + @TableField(exist = false) + private boolean flag = false; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysRoleDept.java b/service/src/main/java/com/rzdata/system/domain/SysRoleDept.java new file mode 100644 index 0000000..69092d1 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysRoleDept.java @@ -0,0 +1,29 @@ +package com.rzdata.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 角色和部门关联 sys_role_dept + * + * @author Lion Li + */ + +@Data +@TableName("sys_role_dept") +public class SysRoleDept { + + /** + * 角色ID + */ + @TableId(type = IdType.INPUT) + private Long roleId; + + /** + * 部门ID + */ + private Long deptId; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysRoleMenu.java b/service/src/main/java/com/rzdata/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..65eadc4 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysRoleMenu.java @@ -0,0 +1,29 @@ +package com.rzdata.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author Lion Li + */ + +@Data +@TableName("sys_role_menu") +public class SysRoleMenu { + + /** + * 角色ID + */ + @TableId(type = IdType.INPUT) + private Long roleId; + + /** + * 菜单ID + */ + private Long menuId; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysUserOnline.java b/service/src/main/java/com/rzdata/system/domain/SysUserOnline.java new file mode 100644 index 0000000..a7f3f38 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysUserOnline.java @@ -0,0 +1,54 @@ +package com.rzdata.system.domain; + +import lombok.Data; + +/** + * 当前在线会话 + * + * @author Lion Li + */ + +@Data +public class SysUserOnline { + + /** + * 会话编号 + */ + private String tokenId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户名称 + */ + private String userName; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysUserPost.java b/service/src/main/java/com/rzdata/system/domain/SysUserPost.java new file mode 100644 index 0000000..77c5bd6 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysUserPost.java @@ -0,0 +1,29 @@ +package com.rzdata.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 用户和岗位关联 sys_user_post + * + * @author Lion Li + */ + +@Data +@TableName("sys_user_post") +public class SysUserPost { + + /** + * 用户ID + */ + @TableId(type = IdType.INPUT) + private Long userId; + + /** + * 岗位ID + */ + private Long postId; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/SysUserRole.java b/service/src/main/java/com/rzdata/system/domain/SysUserRole.java new file mode 100644 index 0000000..715846b --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/SysUserRole.java @@ -0,0 +1,29 @@ +package com.rzdata.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 用户和角色关联 sys_user_role + * + * @author Lion Li + */ + +@Data +@TableName("sys_user_role") +public class SysUserRole { + + /** + * 用户ID + */ + @TableId(type = IdType.INPUT) + private Long userId; + + /** + * 角色ID + */ + private Long roleId; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/bo/SysOssBo.java b/service/src/main/java/com/rzdata/system/domain/bo/SysOssBo.java new file mode 100644 index 0000000..a5cac6b --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/bo/SysOssBo.java @@ -0,0 +1,37 @@ +package com.rzdata.system.domain.bo; + +import com.rzdata.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * OSS对象存储分页查询对象 sys_oss + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SysOssBo extends BaseEntity { + + /** + * 文件名 + */ + private String fileName; + /** + * 原名 + */ + private String originalName; + /** + * 文件后缀名 + */ + private String fileSuffix; + /** + * URL地址 + */ + private String url; + /** + * 服务商 + */ + private String service; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/bo/SysOssConfigBo.java b/service/src/main/java/com/rzdata/system/domain/bo/SysOssConfigBo.java new file mode 100644 index 0000000..36da473 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/bo/SysOssConfigBo.java @@ -0,0 +1,107 @@ +package com.rzdata.system.domain.bo; + +import com.rzdata.common.core.domain.BaseEntity; +import com.rzdata.common.core.validate.AddGroup; +import com.rzdata.common.core.validate.EditGroup; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * 对象存储配置业务对象 sys_oss_config + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class SysOssConfigBo extends BaseEntity { + + /** + * 主建 + */ + @NotNull(message = "主建不能为空", groups = {EditGroup.class}) + private Long ossConfigId; + + /** + * 配置key + */ + @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "configKey长度必须介于2和20 之间") + private String configKey; + + /** + * accessKey + */ + @NotBlank(message = "accessKey不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "accessKey长度必须介于2和100 之间") + private String accessKey; + + /** + * 秘钥 + */ + @NotBlank(message = "secretKey不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "secretKey长度必须介于2和100 之间") + private String secretKey; + + /** + * 桶名称 + */ + @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "bucketName长度必须介于2和100之间") + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "endpoint长度必须介于2和100之间") + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(Y=是,N=否) + */ + private String isHttps; + + /** + * 状态(0=正常,1=停用) + */ + private String status; + + /** + * 域 + */ + private String region; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + @NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String accessPolicy; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/vo/MetaVo.java b/service/src/main/java/com/rzdata/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..bc7c121 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/vo/MetaVo.java @@ -0,0 +1,61 @@ +package com.rzdata.system.domain.vo; + +import com.rzdata.common.utils.StringUtils; +import lombok.Data; + +/** + * 路由显示信息 + * + * @author rzdata + */ + +@Data +public class MetaVo { + + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo(String title, String icon) { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, boolean noCache, String link) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) { + this.link = link; + } + } + +} diff --git a/service/src/main/java/com/rzdata/system/domain/vo/RouterVo.java b/service/src/main/java/com/rzdata/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..f3212ea --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/vo/RouterVo.java @@ -0,0 +1,62 @@ +package com.rzdata.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.util.List; + +/** + * 路由配置信息 + * + * @author Lion Li + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo { + + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/vo/SysOssConfigVo.java b/service/src/main/java/com/rzdata/system/domain/vo/SysOssConfigVo.java new file mode 100644 index 0000000..766ccfe --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/vo/SysOssConfigVo.java @@ -0,0 +1,90 @@ +package com.rzdata.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import lombok.Data; + + +/** + * 对象存储配置视图对象 sys_oss_config + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +@Data +@ExcelIgnoreUnannotated +public class SysOssConfigVo { + + private static final long serialVersionUID = 1L; + + /** + * 主建 + */ + private Long ossConfigId; + + /** + * 配置key + */ + private String configKey; + + /** + * accessKey + */ + private String accessKey; + + /** + * 秘钥 + */ + private String secretKey; + + /** + * 桶名称 + */ + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(Y=是,N=否) + */ + private String isHttps; + + /** + * 域 + */ + private String region; + + /** + * 状态(0=正常,1=停用) + */ + private String status; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + private String accessPolicy; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/vo/SysOssVo.java b/service/src/main/java/com/rzdata/system/domain/vo/SysOssVo.java new file mode 100644 index 0000000..1618bb3 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/vo/SysOssVo.java @@ -0,0 +1,58 @@ +package com.rzdata.system.domain.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * OSS对象存储视图对象 sys_oss + * + * @author Lion Li + */ +@Data +public class SysOssVo { + + private static final long serialVersionUID = 1L; + + /** + * 对象存储主键 + */ + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 上传人 + */ + private String createBy; + + /** + * 服务商 + */ + private String service; + + +} diff --git a/service/src/main/java/com/rzdata/system/domain/vo/SysUserExportVo.java b/service/src/main/java/com/rzdata/system/domain/vo/SysUserExportVo.java new file mode 100644 index 0000000..c275125 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/vo/SysUserExportVo.java @@ -0,0 +1,91 @@ +package com.rzdata.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.convert.ExcelDictConvert; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 用户对象导出VO + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class SysUserExportVo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户序号") + private Long userId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "登录名称") + private String userName; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户名称") + private String nickName; + + /** + * 用户邮箱 + */ + @ExcelProperty(value = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @ExcelProperty(value = "手机号码") + private String phonenumber; + + /** + * 用户性别 + */ + @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 最后登录IP + */ + @ExcelProperty(value = "最后登录IP") + private String loginIp; + + /** + * 最后登录时间 + */ + @ExcelProperty(value = "最后登录时间") + private Date loginDate; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 负责人 + */ + @ExcelProperty(value = "部门负责人") + private String leader; + +} diff --git a/service/src/main/java/com/rzdata/system/domain/vo/SysUserImportVo.java b/service/src/main/java/com/rzdata/system/domain/vo/SysUserImportVo.java new file mode 100644 index 0000000..e5141e4 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/domain/vo/SysUserImportVo.java @@ -0,0 +1,73 @@ +package com.rzdata.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.convert.ExcelDictConvert; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 用户对象导入VO + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +// @Accessors(chain = true) // 导入不允许使用 会找不到set方法 +public class SysUserImportVo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户序号") + private Long userId; + + /** + * 部门ID + */ + @ExcelProperty(value = "部门编号") + private Long deptId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "登录名称") + private String userName; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户名称") + private String nickName; + + /** + * 用户邮箱 + */ + @ExcelProperty(value = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @ExcelProperty(value = "手机号码") + private String phonenumber; + + /** + * 用户性别 + */ + @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + +} diff --git a/service/src/main/java/com/rzdata/system/listener/SysUserImportListener.java b/service/src/main/java/com/rzdata/system/listener/SysUserImportListener.java new file mode 100644 index 0000000..b839414 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/listener/SysUserImportListener.java @@ -0,0 +1,118 @@ +package com.rzdata.system.listener; + +import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.excel.ExcelListener; +import com.rzdata.common.excel.ExcelResult; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.ValidatorUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import com.rzdata.system.domain.vo.SysUserImportVo; +import com.rzdata.system.service.ISysConfigService; +import com.rzdata.system.service.ISysUserService; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * 系统用户自定义导入 + * + * @author Lion Li + */ +@Slf4j +public class SysUserImportListener extends AnalysisEventListener implements ExcelListener { + + private final ISysUserService userService; + + private final String password; + + private final Boolean isUpdateSupport; + + private final String operName; + + private int successNum = 0; + private int failureNum = 0; + private final StringBuilder successMsg = new StringBuilder(); + private final StringBuilder failureMsg = new StringBuilder(); + + public SysUserImportListener(Boolean isUpdateSupport) { + String initPassword = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.user.initPassword"); + this.userService = SpringUtils.getBean(ISysUserService.class); + this.password = BCrypt.hashpw(initPassword); + this.isUpdateSupport = isUpdateSupport; + this.operName = LoginHelper.getUsername(); + } + + @Override + public void invoke(SysUserImportVo userVo, AnalysisContext context) { + SysUser user = this.userService.selectUserByUserName(userVo.getUserName()); + try { + // 验证是否存在这个用户 + if (ObjectUtil.isNull(user)) { + user = BeanUtil.toBean(userVo, SysUser.class); + ValidatorUtils.validate(user); + user.setPassword(password); + user.setCreateBy(operName); + userService.insertUser(user); + successNum++; + successMsg.append("
").append(successNum).append("、账号 ").append(user.getUserName()).append(" 导入成功"); + } else if (isUpdateSupport) { + Long userId = user.getUserId(); + user = BeanUtil.toBean(userVo, SysUser.class); + user.setUserId(userId); + ValidatorUtils.validate(user); + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(operName); + userService.updateUser(user); + successNum++; + successMsg.append("
").append(successNum).append("、账号 ").append(user.getUserName()).append(" 更新成功"); + } else { + failureNum++; + failureMsg.append("
").append(failureNum).append("、账号 ").append(user.getUserName()).append(" 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg).append(e.getMessage()); + log.error(msg, e); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + + @Override + public ExcelResult getExcelResult() { + return new ExcelResult() { + + @Override + public String getAnalysis() { + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List getList() { + return null; + } + + @Override + public List getErrorList() { + return null; + } + }; + } +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysConfigMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..b0d0bb0 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysConfigMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysConfig; + +/** + * 参数配置 数据层 + * + * @author Lion Li + */ +public interface SysConfigMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysDeptMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..9978b50 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysDeptMapper.java @@ -0,0 +1,40 @@ +package com.rzdata.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.rzdata.common.annotation.DataColumn; +import com.rzdata.common.annotation.DataPermission; +import com.rzdata.common.core.domain.entity.SysDept; +import com.rzdata.common.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 部门管理 数据层 + * + * @author Lion Li + */ +public interface SysDeptMapper extends BaseMapperPlus { + + /** + * 查询部门管理数据 + * + * @param queryWrapper 查询条件 + * @return 部门信息集合 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id") + }) + List selectDeptList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysDictDataMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..c9aaf5c --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysDictDataMapper.java @@ -0,0 +1,24 @@ +package com.rzdata.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.entity.SysDictData; +import com.rzdata.common.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 字典表 数据层 + * + * @author Lion Li + */ +public interface SysDictDataMapper extends BaseMapperPlus { + + default List selectDictDataByType(String dictType) { + return selectList( + new LambdaQueryWrapper() + .eq(SysDictData::getStatus, UserConstants.DICT_NORMAL) + .eq(SysDictData::getDictType, dictType) + .orderByAsc(SysDictData::getDictSort)); + } +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysDictTypeMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..5093bd2 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.domain.entity.SysDictType; +import com.rzdata.common.core.mapper.BaseMapperPlus; + +/** + * 字典表 数据层 + * + * @author Lion Li + */ +public interface SysDictTypeMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysLogininforMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..dd33639 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysLogininforMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author Lion Li + */ +public interface SysLogininforMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysMenuMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..514bd4b --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysMenuMapper.java @@ -0,0 +1,82 @@ +package com.rzdata.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.entity.SysMenu; +import com.rzdata.common.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 菜单表 数据层 + * + * @author Lion Li + */ +public interface SysMenuMapper extends BaseMapperPlus { + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param queryWrapper 查询条件 + * @return 菜单列表 + */ + List selectMenuListByUserId(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + List selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + default List selectMenuTreeAll() { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .in(SysMenu::getMenuType, UserConstants.TYPE_DIR, UserConstants.TYPE_MENU) + .eq(SysMenu::getStatus, UserConstants.MENU_NORMAL) + .orderByAsc(SysMenu::getParentId) + .orderByAsc(SysMenu::getOrderNum); + return this.selectList(lqw); + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysNoticeMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..30956f6 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysNoticeMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author Lion Li + */ +public interface SysNoticeMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysOperLogMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..9a77989 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysOperLogMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysOperLog; + +/** + * 操作日志 数据层 + * + * @author Lion Li + */ +public interface SysOperLogMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysOssConfigMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysOssConfigMapper.java new file mode 100644 index 0000000..c816979 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysOssConfigMapper.java @@ -0,0 +1,16 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysOssConfig; +import com.rzdata.system.domain.vo.SysOssConfigVo; + +/** + * 对象存储配置Mapper接口 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +public interface SysOssConfigMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysOssMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysOssMapper.java new file mode 100644 index 0000000..6004689 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysOssMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysOss; +import com.rzdata.system.domain.vo.SysOssVo; + +/** + * 文件上传 数据层 + * + * @author Lion Li + */ +public interface SysOssMapper extends BaseMapperPlus { +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysPostMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..310edd7 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysPostMapper.java @@ -0,0 +1,31 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 数据层 + * + * @author Lion Li + */ +public interface SysPostMapper extends BaseMapperPlus { + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + List selectPostsByUserName(String userName); + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysRoleDeptMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..fd45226 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author Lion Li + */ +public interface SysRoleDeptMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysRoleMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..24e73d5 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysRoleMapper.java @@ -0,0 +1,62 @@ +package com.rzdata.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.annotation.DataColumn; +import com.rzdata.common.annotation.DataPermission; +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 角色表 数据层 + * + * @author Lion Li + */ +public interface SysRoleMapper extends BaseMapperPlus { + + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id") + }) + Page selectPageRoleList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询角色数据 + * + * @param queryWrapper 查询条件 + * @return 角色数据集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id") + }) + List selectRoleList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolePermissionByUserId(Long userId); + + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + List selectRoleListByUserId(Long userId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + List selectRolesByUserName(String userName); + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysRoleMenuMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..e1a49c4 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author Lion Li + */ +public interface SysRoleMenuMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysUserMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..6d27618 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysUserMapper.java @@ -0,0 +1,87 @@ +package com.rzdata.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.annotation.DataColumn; +import com.rzdata.common.annotation.DataPermission; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户表 数据层 + * + * @author Lion Li + */ +public interface SysUserMapper extends BaseMapperPlus { + + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectPageUserList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询用户列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectAllocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectUnallocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + SysUser selectUserByUserName(String userName); + + /** + * 通过手机号查询用户 + * + * @param phonenumber 手机号 + * @return 用户对象信息 + */ + SysUser selectUserByPhonenumber(String phonenumber); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + SysUser selectUserById(Long userId); + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysUserPostMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..b46d583 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysUserPostMapper.java @@ -0,0 +1,13 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author Lion Li + */ +public interface SysUserPostMapper extends BaseMapperPlus { + +} diff --git a/service/src/main/java/com/rzdata/system/mapper/SysUserRoleMapper.java b/service/src/main/java/com/rzdata/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..43907c2 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,17 @@ +package com.rzdata.system.mapper; + +import com.rzdata.common.core.mapper.BaseMapperPlus; +import com.rzdata.system.domain.SysUserRole; + +import java.util.List; + +/** + * 用户与角色关联表 数据层 + * + * @author Lion Li + */ +public interface SysUserRoleMapper extends BaseMapperPlus { + + List selectUserIdsByRoleId(Long roleId); + +} diff --git a/service/src/main/java/com/rzdata/system/runner/SystemApplicationRunner.java b/service/src/main/java/com/rzdata/system/runner/SystemApplicationRunner.java new file mode 100644 index 0000000..dd75bf1 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/runner/SystemApplicationRunner.java @@ -0,0 +1,34 @@ +package com.rzdata.system.runner; + +import com.rzdata.common.config.RuoYiConfig; +import com.rzdata.system.service.ISysConfigService; +import com.rzdata.system.service.ISysDictTypeService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +/** + * 初始化 system 模块对应业务数据 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor +@Component +public class SystemApplicationRunner implements ApplicationRunner { + + private final RuoYiConfig ruoyiConfig; + private final ISysConfigService configService; + private final ISysDictTypeService dictTypeService; + + @Override + public void run(ApplicationArguments args) throws Exception { + configService.loadingConfigCache(); + log.info("加载参数缓存数据成功"); + dictTypeService.loadingDictCache(); + log.info("加载字典缓存数据成功"); + } + +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysConfigService.java b/service/src/main/java/com/rzdata/system/service/ISysConfigService.java new file mode 100644 index 0000000..c9028c9 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysConfigService.java @@ -0,0 +1,96 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.system.domain.SysConfig; + +import java.util.List; + +/** + * 参数配置 服务层 + * + * @author Lion Li + */ +public interface ISysConfigService { + + + TableDataInfo selectPageConfigList(SysConfig config, PageQuery pageQuery); + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + String selectConfigByKey(String configKey); + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + boolean selectCaptchaEnabled(); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + List selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + String insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + String updateConfig(SysConfig config); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + void deleteConfigByIds(Long[] configIds); + + /** + * 加载参数缓存数据 + */ + void loadingConfigCache(); + + /** + * 清空参数缓存数据 + */ + void clearConfigCache(); + + /** + * 重置参数缓存数据 + */ + void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + String checkConfigKeyUnique(SysConfig config); + +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysDataScopeService.java b/service/src/main/java/com/rzdata/system/service/ISysDataScopeService.java new file mode 100644 index 0000000..3941daa --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysDataScopeService.java @@ -0,0 +1,26 @@ +package com.rzdata.system.service; + +/** + * 通用 数据权限 服务 + * + * @author Lion Li + */ +public interface ISysDataScopeService { + + /** + * 获取角色自定义权限 + * + * @param roleId 角色id + * @return 部门id组 + */ + String getRoleCustom(Long roleId); + + /** + * 获取部门及以下权限 + * + * @param deptId 部门id + * @return 部门id组 + */ + String getDeptAndChild(Long deptId); + +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysDeptService.java b/service/src/main/java/com/rzdata/system/service/ISysDeptService.java new file mode 100644 index 0000000..d7a7ac4 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysDeptService.java @@ -0,0 +1,116 @@ +package com.rzdata.system.service; + +import cn.hutool.core.lang.tree.Tree; +import com.rzdata.common.core.domain.entity.SysDept; + +import java.util.List; + +/** + * 部门管理 服务层 + * + * @author Lion Li + */ +public interface ISysDeptService { + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + List selectDeptList(SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + List> selectDeptTreeList(SysDept dept); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + List> buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门数(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + long selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + String checkDeptNameUnique(SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + int deleteDeptById(Long deptId); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysDictDataService.java b/service/src/main/java/com/rzdata/system/service/ISysDictDataService.java new file mode 100644 index 0000000..e26b22a --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysDictDataService.java @@ -0,0 +1,66 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.entity.SysDictData; +import com.rzdata.common.core.page.TableDataInfo; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author Lion Li + */ +public interface ISysDictDataService { + + + TableDataInfo selectPageDictDataList(SysDictData dictData, PageQuery pageQuery); + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + List insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + List updateDictData(SysDictData dictData); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysDictTypeService.java b/service/src/main/java/com/rzdata/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..f6c7532 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysDictTypeService.java @@ -0,0 +1,104 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.entity.SysDictData; +import com.rzdata.common.core.domain.entity.SysDictType; +import com.rzdata.common.core.page.TableDataInfo; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author Lion Li + */ +public interface ISysDictTypeService { + + + TableDataInfo selectPageDictTypeList(SysDictType dictType, PageQuery pageQuery); + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + List insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + List updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + String checkDictTypeUnique(SysDictType dictType); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysLogininforService.java b/service/src/main/java/com/rzdata/system/service/ISysLogininforService.java new file mode 100644 index 0000000..7e33a1a --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysLogininforService.java @@ -0,0 +1,46 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.system.domain.SysLogininfor; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层 + * + * @author Lion Li + */ +public interface ISysLogininforService { + + + TableDataInfo selectPageLogininforList(SysLogininfor logininfor, PageQuery pageQuery); + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + void cleanLogininfor(); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysMenuService.java b/service/src/main/java/com/rzdata/system/service/ISysMenuService.java new file mode 100644 index 0000000..ee7dee3 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysMenuService.java @@ -0,0 +1,137 @@ +package com.rzdata.system.service; + +import cn.hutool.core.lang.tree.Tree; +import com.rzdata.common.core.domain.entity.SysMenu; +import com.rzdata.system.domain.vo.RouterVo; + +import java.util.List; +import java.util.Set; + +/** + * 菜单 业务层 + * + * @author Lion Li + */ +public interface ISysMenuService { + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + List selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + List buildMenus(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + List> buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + String checkMenuNameUnique(SysMenu menu); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysNoticeService.java b/service/src/main/java/com/rzdata/system/service/ISysNoticeService.java new file mode 100644 index 0000000..26cc381 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysNoticeService.java @@ -0,0 +1,66 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.system.domain.SysNotice; + +import java.util.List; + +/** + * 公告 服务层 + * + * @author Lion Li + */ +public interface ISysNoticeService { + + + TableDataInfo selectPageNoticeList(SysNotice notice, PageQuery pageQuery); + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysOperLogService.java b/service/src/main/java/com/rzdata/system/service/ISysOperLogService.java new file mode 100644 index 0000000..23e2d68 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysOperLogService.java @@ -0,0 +1,53 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.system.domain.SysOperLog; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author Lion Li + */ +public interface ISysOperLogService { + + TableDataInfo selectPageOperLogList(SysOperLog operLog, PageQuery pageQuery); + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + void cleanOperLog(); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysOssService.java b/service/src/main/java/com/rzdata/system/service/ISysOssService.java new file mode 100644 index 0000000..6d4dfae --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysOssService.java @@ -0,0 +1,33 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.system.domain.bo.SysOssBo; +import com.rzdata.system.domain.vo.SysOssVo; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +/** + * 文件上传 服务层 + * + * @author Lion Li + */ +public interface ISysOssService { + + TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); + + List listByIds(Collection ossIds); + + SysOssVo getById(Long ossId); + + SysOssVo upload(MultipartFile file); + + void download(Long ossId, HttpServletResponse response) throws IOException; + + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysPostService.java b/service/src/main/java/com/rzdata/system/service/ISysPostService.java new file mode 100644 index 0000000..9c10121 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysPostService.java @@ -0,0 +1,105 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 服务层 + * + * @author Lion Li + */ +public interface ISysPostService { + + + TableDataInfo selectPagePostList(SysPost post, PageQuery pageQuery); + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + String checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + String checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + long countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + int updatePost(SysPost post); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysRoleService.java b/service/src/main/java/com/rzdata/system/service/ISysRoleService.java new file mode 100644 index 0000000..8e41791 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysRoleService.java @@ -0,0 +1,179 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.system.domain.SysUserRole; + +import java.util.List; +import java.util.Set; + +/** + * 角色业务层 + * + * @author Lion Li + */ +public interface ISysRoleService { + + + TableDataInfo selectPageRoleList(SysRole role, PageQuery pageQuery); + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + String checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + String checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + void checkRoleAllowed(SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + void checkRoleDataScope(Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + long countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + int insertAuthUsers(Long roleId, Long[] userIds); +} diff --git a/service/src/main/java/com/rzdata/system/service/ISysUserService.java b/service/src/main/java/com/rzdata/system/service/ISysUserService.java new file mode 100644 index 0000000..93c3254 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/ISysUserService.java @@ -0,0 +1,211 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.core.page.TableDataInfo; + +import java.util.List; + +/** + * 用户 业务层 + * + * @author Lion Li + */ +public interface ISysUserService { + + + TableDataInfo selectPageUserList(SysUser user, PageQuery pageQuery); + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + List selectUserList(SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + TableDataInfo selectAllocatedList(SysUser user, PageQuery pageQuery); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + TableDataInfo selectUnallocatedList(SysUser user, PageQuery pageQuery); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + SysUser selectUserByUserName(String userName); + + /** + * 通过手机号查询用户 + * + * @param phonenumber 手机号 + * @return 用户对象信息 + */ + SysUser selectUserByPhonenumber(String phonenumber); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + String checkUserNameUnique(SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + String checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + String checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + int insertUser(SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + int updateUser(SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + int deleteUserByIds(Long[] userIds); + +} diff --git a/service/src/main/java/com/rzdata/system/service/SysLoginService.java b/service/src/main/java/com/rzdata/system/service/SysLoginService.java new file mode 100644 index 0000000..a6d3985 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/SysLoginService.java @@ -0,0 +1,294 @@ +package com.rzdata.system.service; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.secure.BCrypt; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.constant.Constants; +import com.rzdata.common.core.domain.event.LogininforEvent; +import com.rzdata.common.core.domain.dto.RoleDTO; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.core.domain.model.XcxLoginUser; +import com.rzdata.common.enums.DeviceType; +import com.rzdata.common.enums.LoginType; +import com.rzdata.common.enums.UserStatus; +import com.rzdata.common.exception.user.CaptchaException; +import com.rzdata.common.exception.user.CaptchaExpireException; +import com.rzdata.common.exception.user.UserException; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.DateUtils; +import com.rzdata.common.utils.MessageUtils; +import com.rzdata.common.utils.ServletUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.RedisUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import com.rzdata.system.mapper.SysUserMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import java.time.Duration; +import java.util.List; +import java.util.function.Supplier; + +/** + * 登录校验方法 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class SysLoginService { + + private final SysUserMapper userMapper; + private final ISysConfigService configService; + private final SysPermissionService permissionService; + + @Value("${user.password.maxRetryCount}") + private Integer maxRetryCount; + + @Value("${user.password.lockTime}") + private Integer lockTime; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) { + HttpServletRequest request = ServletUtils.getRequest(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + // 验证码开关 + if (captchaEnabled) { + validateCaptcha(username, code, uuid, request); + } + SysUser user = loadUserByUsername(username); + checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword())); + // 此处可根据登录用户的数据不同 自行创建 loginUser + LoginUser loginUser = buildLoginUser(user); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.PC); + + recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + recordLoginInfo(user.getUserId(), username); + return StpUtil.getTokenValue(); + } + + public String smsLogin(String phonenumber, String smsCode) { + // 通过手机号查找用户 + SysUser user = loadUserByPhonenumber(phonenumber); + + checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser + LoginUser loginUser = buildLoginUser(user); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.APP); + + recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + recordLoginInfo(user.getUserId(), user.getUserName()); + return StpUtil.getTokenValue(); + } + + + public String xcxLogin(String xcxCode) { + // xcxCode 为 小程序调用 wx.login 授权后获取 + // todo 以下自行实现 + // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid + String openid = ""; + SysUser user = loadUserByOpenid(openid); + + // 此处可根据登录用户的数据不同 自行创建 loginUser + XcxLoginUser loginUser = new XcxLoginUser(); + loginUser.setUserId(user.getUserId()); + loginUser.setUsername(user.getUserName()); + loginUser.setUserType(user.getUserType()); + loginUser.setOpenid(openid); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.XCX); + + recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + recordLoginInfo(user.getUserId(), user.getUserName()); + return StpUtil.getTokenValue(); + } + + /** + * 退出登录 + */ + public void logout() { + try { + LoginUser loginUser = LoginHelper.getLoginUser(); + StpUtil.logout(); + recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); + } catch (NotLoginException ignored) { + } + } + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * @return + */ + private void recordLogininfor(String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + + /** + * 校验短信验证码 + */ + private boolean validateSmsCode(String phonenumber, String smsCode) { + String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + phonenumber); + if (StringUtils.isBlank(code)) { + recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + return code.equals(smsCode); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + */ + public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); + String captcha = RedisUtils.getCacheObject(verifyKey); + RedisUtils.deleteObject(verifyKey); + if (captcha == null) { + recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); + throw new CaptchaException(); + } + } + + private SysUser loadUserByUsername(String username) { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getUserName, SysUser::getStatus) + .eq(SysUser::getUserName, username)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new UserException("user.not.exists", username); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new UserException("user.blocked", username); + } + return userMapper.selectUserByUserName(username); + } + + private SysUser loadUserByPhonenumber(String phonenumber) { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber, SysUser::getStatus) + .eq(SysUser::getPhonenumber, phonenumber)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", phonenumber); + throw new UserException("user.not.exists", phonenumber); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", phonenumber); + throw new UserException("user.blocked", phonenumber); + } + return userMapper.selectUserByPhonenumber(phonenumber); + } + + private SysUser loadUserByOpenid(String openid) { + // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 + // todo 自行实现 userService.selectUserByOpenid(openid); + SysUser user = new SysUser(); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", openid); + // todo 用户不存在 业务逻辑自行实现 + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", openid); + // todo 用户已被停用 业务逻辑自行实现 + } + return user; + } + + /** + * 构建登录用户 + */ + private LoginUser buildLoginUser(SysUser user) { + LoginUser loginUser = new LoginUser(); + loginUser.setUserId(user.getUserId()); + loginUser.setDeptId(user.getDeptId()); + loginUser.setUsername(user.getUserName()); + loginUser.setUserType(user.getUserType()); + loginUser.setMenuPermission(permissionService.getMenuPermission(user)); + loginUser.setRolePermission(permissionService.getRolePermission(user)); + loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName()); + List roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class); + loginUser.setRoles(roles); + return loginUser; + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId, String username) { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(ServletUtils.getClientIP()); + sysUser.setLoginDate(DateUtils.getNowDate()); + sysUser.setUpdateBy(username); + userMapper.updateById(sysUser); + } + + /** + * 登录校验 + */ + private void checkLogin(LoginType loginType, String username, Supplier supplier) { + String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username; + String loginFail = Constants.LOGIN_FAIL; + + // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip) + Integer errorNumber = RedisUtils.getCacheObject(errorKey); + // 锁定时间内登录 则踢出 + if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) { + recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); + throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); + } + + if (supplier.get()) { + // 是否第一次 + errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1; + // 达到规定错误次数 则锁定登录 + if (errorNumber.equals(maxRetryCount)) { + RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); + recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); + throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); + } else { + // 未达到规定错误次数 则递增 + RedisUtils.setCacheObject(errorKey, errorNumber); + recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); + throw new UserException(loginType.getRetryLimitCount(), errorNumber); + } + } + + // 登录成功 清空错误次数 + RedisUtils.deleteObject(errorKey); + } +} diff --git a/service/src/main/java/com/rzdata/system/service/SysPermissionService.java b/service/src/main/java/com/rzdata/system/service/SysPermissionService.java new file mode 100644 index 0000000..0809145 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/SysPermissionService.java @@ -0,0 +1,67 @@ +package com.rzdata.system.service; + +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.core.domain.entity.SysUser; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 用户权限处理 + * + * @author rzdata + */ +@RequiredArgsConstructor +@Service +public class SysPermissionService { + + private final ISysRoleService roleService; + private final ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) { + roles.add("admin"); + } else { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) { + perms.add("*:*:*"); + } else { + List roles = user.getRoles(); + if (!roles.isEmpty() && roles.size() > 1) { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } else { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/service/src/main/java/com/rzdata/system/service/SysRegisterService.java b/service/src/main/java/com/rzdata/system/service/SysRegisterService.java new file mode 100644 index 0000000..e054255 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/SysRegisterService.java @@ -0,0 +1,106 @@ +package com.rzdata.system.service; + +import cn.dev33.satoken.secure.BCrypt; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.constant.Constants; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.event.LogininforEvent; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.core.domain.model.RegisterBody; +import com.rzdata.common.enums.UserType; +import com.rzdata.common.exception.user.CaptchaException; +import com.rzdata.common.exception.user.CaptchaExpireException; +import com.rzdata.common.exception.user.UserException; +import com.rzdata.common.utils.MessageUtils; +import com.rzdata.common.utils.ServletUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.RedisUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; + +/** + * 注册校验方法 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysRegisterService { + + private final ISysUserService userService; + private final ISysConfigService configService; + + /** + * 注册 + */ + public void register(RegisterBody registerBody) { + HttpServletRequest request = ServletUtils.getRequest(); + String username = registerBody.getUsername(); + String password = registerBody.getPassword(); + // 校验用户类型是否存在 + String userType = UserType.getUserType(registerBody.getUserType()).getUserType(); + + boolean captchaEnabled = configService.selectCaptchaEnabled(); + // 验证码开关 + if (captchaEnabled) { + validateCaptcha(username, registerBody.getCode(), registerBody.getUuid(), request); + } + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + sysUser.setNickName(username); + sysUser.setPassword(BCrypt.hashpw(password)); + sysUser.setUserType(userType); + + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser))) { + throw new UserException("user.register.save.error", username); + } + boolean regFlag = userService.registerUser(sysUser); + if (!regFlag) { + throw new UserException("user.register.error"); + } + recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success")); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); + String captcha = RedisUtils.getCacheObject(verifyKey); + RedisUtils.deleteObject(verifyKey); + if (captcha == null) { + recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error")); + throw new CaptchaException(); + } + } + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * @return + */ + private void recordLogininfor(String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysConfigServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..5f401cd --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,227 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.constant.CacheNames; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.core.service.ConfigService; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.CacheUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import com.rzdata.system.domain.SysConfig; +import com.rzdata.system.mapper.SysConfigMapper; +import com.rzdata.system.service.ISysConfigService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 参数配置 服务层实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysConfigServiceImpl implements ISysConfigService, ConfigService { + + private final SysConfigMapper baseMapper; + + @Override + public TableDataInfo selectPageConfigList(SysConfig config, PageQuery pageQuery) { + Map params = config.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName()) + .eq(StringUtils.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType()) + .like(StringUtils.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysConfig::getCreateTime, params.get("beginTime"), params.get("endTime")); + Page page = baseMapper.selectPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DS("master") + public SysConfig selectConfigById(Long configId) { + return baseMapper.selectById(configId); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey") + @Override + public String selectConfigByKey(String configKey) { + SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, configKey)); + if (ObjectUtil.isNotNull(retConfig)) { + return retConfig.getConfigValue(); + } + return StringUtils.EMPTY; + } + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + @Override + public boolean selectCaptchaEnabled() { + String captchaEnabled = SpringUtils.getAopProxy(this).selectConfigByKey("sys.account.captchaEnabled"); + if (StringUtils.isEmpty(captchaEnabled)) { + return true; + } + return Convert.toBool(captchaEnabled); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfig config) { + Map params = config.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName()) + .eq(StringUtils.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType()) + .like(StringUtils.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysConfig::getCreateTime, params.get("beginTime"), params.get("endTime")); + return baseMapper.selectList(lqw); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#config.configKey") + @Override + public String insertConfig(SysConfig config) { + int row = baseMapper.insert(config); + if (row > 0) { + return config.getConfigValue(); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#config.configKey") + @Override + public String updateConfig(SysConfig config) { + int row = 0; + if (config.getConfigId() != null) { + SysConfig temp = baseMapper.selectById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) { + CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey()); + } + row = baseMapper.updateById(config); + } else { + row = baseMapper.update(config, new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, config.getConfigKey())); + } + if (row > 0) { + return config.getConfigValue(); + } + throw new ServiceException("操作失败"); + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) { + for (Long configId : configIds) { + SysConfig config = selectConfigById(configId); + if (StringUtils.equals(UserConstants.YES, config.getConfigType())) { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); + } + baseMapper.deleteBatchIds(Arrays.asList(configIds)); + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() { + List configsList = selectConfigList(new SysConfig()); + configsList.forEach(config -> + CacheUtils.put(CacheNames.SYS_CONFIG, config.getConfigKey(), config.getConfigValue())); + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache() { + CacheUtils.clear(CacheNames.SYS_CONFIG); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() { + clearConfigCache(); + loadingConfigCache(); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public String checkConfigKeyUnique(SysConfig config) { + Long configId = ObjectUtil.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = baseMapper.selectOne(new LambdaQueryWrapper().eq(SysConfig::getConfigKey, config.getConfigKey())); + if (ObjectUtil.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 根据参数 key 获取参数值 + * + * @param configKey 参数 key + * @return 参数值 + */ + @Override + public String getConfigValue(String configKey) { + return SpringUtils.getAopProxy(this).selectConfigByKey(configKey); + } + +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysDataScopeServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysDataScopeServiceImpl.java new file mode 100644 index 0000000..6282706 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysDataScopeServiceImpl.java @@ -0,0 +1,61 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.rzdata.common.core.domain.entity.SysDept; +import com.rzdata.common.helper.DataBaseHelper; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.system.domain.SysRoleDept; +import com.rzdata.system.mapper.SysDeptMapper; +import com.rzdata.system.mapper.SysRoleDeptMapper; +import com.rzdata.system.service.ISysDataScopeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 数据权限 实现 + *

+ * 注意: 此Service内不允许调用标注`数据权限`注解的方法 + * 例如: deptMapper.selectList 此 selectList 方法标注了`数据权限`注解 会出现循环解析的问题 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service("sdss") +public class SysDataScopeServiceImpl implements ISysDataScopeService { + + private final SysRoleDeptMapper roleDeptMapper; + private final SysDeptMapper deptMapper; + + @Override + public String getRoleCustom(Long roleId) { + List list = roleDeptMapper.selectList( + new LambdaQueryWrapper() + .select(SysRoleDept::getDeptId) + .eq(SysRoleDept::getRoleId, roleId)); + if (CollUtil.isNotEmpty(list)) { + return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId())); + } + return null; + } + + @Override + public String getDeptAndChild(Long deptId) { + List deptList = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(deptId); + List list = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .in(SysDept::getDeptId, ids)); + if (CollUtil.isNotEmpty(list)) { + return StreamUtils.join(list, d -> Convert.toStr(d.getDeptId())); + } + return null; + } + +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysDeptServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..e97dace --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,278 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.entity.SysDept; +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.helper.DataBaseHelper; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.TreeBuildUtils; +import com.rzdata.system.mapper.SysDeptMapper; +import com.rzdata.system.mapper.SysRoleMapper; +import com.rzdata.system.mapper.SysUserMapper; +import com.rzdata.system.service.ISysDeptService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 部门管理 服务实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDeptServiceImpl implements ISysDeptService { + + private final SysDeptMapper baseMapper; + private final SysRoleMapper roleMapper; + private final SysUserMapper userMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + public List selectDeptList(SysDept dept) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(SysDept::getDelFlag, "0") + .eq(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId()) + .eq(ObjectUtil.isNotNull(dept.getParentId()), SysDept::getParentId, dept.getParentId()) + .like(StringUtils.isNotBlank(dept.getDeptName()), SysDept::getDeptName, dept.getDeptName()) + .eq(StringUtils.isNotBlank(dept.getStatus()), SysDept::getStatus, dept.getStatus()) + .orderByAsc(SysDept::getParentId) + .orderByAsc(SysDept::getOrderNum); + return baseMapper.selectDeptList(lqw); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + @Override + public List> selectDeptTreeList(SysDept dept) { + List depts = this.selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List> buildDeptTreeSelect(List depts) { + if (CollUtil.isEmpty(depts)) { + return CollUtil.newArrayList(); + } + return TreeBuildUtils.build(depts, (dept, tree) -> + tree.setId(dept.getDeptId()) + .setParentId(dept.getParentId()) + .setName(dept.getDeptName()) + .setWeight(dept.getOrderNum())); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) { + SysRole role = roleMapper.selectById(roleId); + return baseMapper.selectDeptListByRoleId(roleId, role.getDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) { + SysDept dept = baseMapper.selectById(deptId); + SysDept parentDept = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysDept::getDeptName).eq(SysDept::getDeptId, dept.getParentId())); + dept.setParentName(ObjectUtil.isNotNull(parentDept) ? parentDept.getDeptName() : null); + return dept; + } + + /** + * 根据ID查询所有子部门数(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public long selectNormalChildrenDeptById(Long deptId) { + return baseMapper.selectCount(new LambdaQueryWrapper() + .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL) + .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) { + return baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDept::getParentId, deptId)); + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) { + return userMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getDeptId, deptId)); + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public String checkDeptNameUnique(SysDept dept) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDept::getDeptName, dept.getDeptName()) + .eq(SysDept::getParentId, dept.getParentId()) + .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) { + if (!LoginHelper.isAdmin()) { + SysDept dept = new SysDept(); + dept.setDeptId(deptId); + List depts = this.selectDeptList(dept); + if (CollUtil.isEmpty(depts)) { + throw new ServiceException("没有权限访问部门数据!"); + } + } + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) { + SysDept info = baseMapper.selectById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) { + throw new ServiceException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return baseMapper.insert(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) { + SysDept newParentDept = baseMapper.selectById(dept.getParentId()); + SysDept oldDept = baseMapper.selectById(dept.getDeptId()); + if (ObjectUtil.isNotNull(newParentDept) && ObjectUtil.isNotNull(oldDept)) { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = baseMapper.updateById(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals(UserConstants.DEPT_NORMAL, dept.getAncestors())) { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + baseMapper.update(null, new LambdaUpdateWrapper() + .set(SysDept::getStatus, UserConstants.DEPT_NORMAL) + .in(SysDept::getDeptId, Arrays.asList(deptIds))); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) { + List children = baseMapper.selectList(new LambdaQueryWrapper() + .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + List list = new ArrayList<>(); + for (SysDept child : children) { + SysDept dept = new SysDept(); + dept.setDeptId(child.getDeptId()); + dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + list.add(dept); + } + if (list.size() > 0) { + baseMapper.updateBatchById(list); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) { + return baseMapper.deleteById(deptId); + } + +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysDictDataServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..49a9a0e --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,130 @@ +package com.rzdata.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.constant.CacheNames; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.entity.SysDictData; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.CacheUtils; +import com.rzdata.system.mapper.SysDictDataMapper; +import com.rzdata.system.service.ISysDictDataService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 字典 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDictDataServiceImpl implements ISysDictDataService { + + private final SysDictDataMapper baseMapper; + + @Override + public TableDataInfo selectPageDictDataList(SysDictData dictData, PageQuery pageQuery) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .eq(StringUtils.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType()) + .like(StringUtils.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel()) + .eq(StringUtils.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus()) + .orderByAsc(SysDictData::getDictSort); + Page page = baseMapper.selectPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictData dictData) { + return baseMapper.selectList(new LambdaQueryWrapper() + .eq(StringUtils.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType()) + .like(StringUtils.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel()) + .eq(StringUtils.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus()) + .orderByAsc(SysDictData::getDictSort)); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) { + return baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysDictData::getDictLabel) + .eq(SysDictData::getDictType, dictType) + .eq(SysDictData::getDictValue, dictValue)) + .getDictLabel(); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) { + return baseMapper.selectById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) { + for (Long dictCode : dictCodes) { + SysDictData data = selectDictDataById(dictCode); + baseMapper.deleteById(dictCode); + CacheUtils.evict(CacheNames.SYS_DICT, data.getDictType()); + } + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#data.dictType") + @Override + public List insertDictData(SysDictData data) { + int row = baseMapper.insert(data); + if (row > 0) { + return baseMapper.selectDictDataByType(data.getDictType()); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#data.dictType") + @Override + public List updateDictData(SysDictData data) { + int row = baseMapper.updateById(data); + if (row > 0) { + return baseMapper.selectDictDataByType(data.getDictType()); + } + throw new ServiceException("操作失败"); + } + +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysDictTypeServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..7948ef6 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,285 @@ +package com.rzdata.system.service.impl; + +import cn.dev33.satoken.context.SaHolder; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.constant.CacheNames; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.entity.SysDictData; +import com.rzdata.common.core.domain.entity.SysDictType; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.core.service.DictService; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.CacheUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import com.rzdata.system.mapper.SysDictDataMapper; +import com.rzdata.system.mapper.SysDictTypeMapper; +import com.rzdata.system.service.ISysDictTypeService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 字典 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService { + + private final SysDictTypeMapper baseMapper; + private final SysDictDataMapper dictDataMapper; + + @Override + public TableDataInfo selectPageDictTypeList(SysDictType dictType, PageQuery pageQuery) { + Map params = dictType.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName()) + .eq(StringUtils.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus()) + .like(StringUtils.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysDictType::getCreateTime, params.get("beginTime"), params.get("endTime")); + Page page = baseMapper.selectPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictType dictType) { + Map params = dictType.getParams(); + return baseMapper.selectList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName()) + .eq(StringUtils.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus()) + .like(StringUtils.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysDictType::getCreateTime, params.get("beginTime"), params.get("endTime"))); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() { + return baseMapper.selectList(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType") + @Override + public List selectDictDataByType(String dictType) { + List dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (CollUtil.isNotEmpty(dictDatas)) { + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) { + return baseMapper.selectById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType") + @Override + public SysDictType selectDictTypeByType(String dictType) { + return baseMapper.selectById(new LambdaQueryWrapper().eq(SysDictType::getDictType, dictType)); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) { + for (Long dictId : dictIds) { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.exists(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, dictType.getDictType()))) { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType()); + } + baseMapper.deleteBatchIds(Arrays.asList(dictIds)); + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() { + List dictDataList = dictDataMapper.selectList( + new LambdaQueryWrapper().eq(SysDictData::getStatus, UserConstants.DICT_NORMAL)); + Map> dictDataMap = StreamUtils.groupByKey(dictDataList, SysDictData::getDictType); + dictDataMap.forEach((k,v) -> { + List dictList = StreamUtils.sorted(v, Comparator.comparing(SysDictData::getDictSort)); + CacheUtils.put(CacheNames.SYS_DICT, k, dictList); + }); + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache() { + CacheUtils.clear(CacheNames.SYS_DICT); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#dict.dictType") + @Override + public List insertDictType(SysDictType dict) { + int row = baseMapper.insert(dict); + if (row > 0) { + return new ArrayList<>(); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#dict.dictType") + @Override + @Transactional(rollbackFor = Exception.class) + public List updateDictType(SysDictType dict) { + SysDictType oldDict = baseMapper.selectById(dict.getDictId()); + dictDataMapper.update(null, new LambdaUpdateWrapper() + .set(SysDictData::getDictType, dict.getDictType()) + .eq(SysDictData::getDictType, oldDict.getDictType())); + int row = baseMapper.updateById(dict); + if (row > 0) { + CacheUtils.evict(CacheNames.SYS_DICT, oldDict.getDictType()); + return dictDataMapper.selectDictDataByType(dict.getDictType()); + } + throw new ServiceException("操作失败"); + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public String checkDictTypeUnique(SysDictType dict) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDictType::getDictType, dict.getDictType()) + .ne(ObjectUtil.isNotNull(dict.getDictId()), SysDictType::getDictId, dict.getDictId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + @SuppressWarnings("unchecked cast") + @Override + public String getDictLabel(String dictType, String dictValue, String separator) { + // 优先从本地缓存获取 + List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); + if (ObjectUtil.isNull(datas)) { + datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); + } + + Map map = StreamUtils.toMap(datas, SysDictData::getDictValue, SysDictData::getDictLabel); + if (StringUtils.containsAny(dictValue, separator)) { + return Arrays.stream(dictValue.split(separator)) + .map(v -> map.getOrDefault(v, StringUtils.EMPTY)) + .collect(Collectors.joining(separator)); + } else { + return map.getOrDefault(dictValue, StringUtils.EMPTY); + } + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + @SuppressWarnings("unchecked cast") + @Override + public String getDictValue(String dictType, String dictLabel, String separator) { + // 优先从本地缓存获取 + List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); + if (ObjectUtil.isNull(datas)) { + datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); + } + + Map map = StreamUtils.toMap(datas, SysDictData::getDictLabel, SysDictData::getDictValue); + if (StringUtils.containsAny(dictLabel, separator)) { + return Arrays.stream(dictLabel.split(separator)) + .map(l -> map.getOrDefault(l, StringUtils.EMPTY)) + .collect(Collectors.joining(separator)); + } else { + return map.getOrDefault(dictLabel, StringUtils.EMPTY); + } + } + +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysLogininforServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..08f791f --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,155 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.constant.Constants; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.event.LogininforEvent; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.utils.ServletUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.ip.AddressUtils; +import com.rzdata.system.domain.SysLogininfor; +import com.rzdata.system.mapper.SysLogininforMapper; +import com.rzdata.system.service.ISysLogininforService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class SysLogininforServiceImpl implements ISysLogininforService { + + private final SysLogininforMapper baseMapper; + + /** + * 记录登录信息 + * + * @param logininforEvent 登录事件 + */ + @Async + @EventListener + public void recordLogininfor(LogininforEvent logininforEvent) { + HttpServletRequest request = logininforEvent.getRequest(); + final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); + final String ip = ServletUtils.getClientIP(request); + + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(getBlock(ip)); + s.append(address); + s.append(getBlock(logininforEvent.getUsername())); + s.append(getBlock(logininforEvent.getStatus())); + s.append(getBlock(logininforEvent.getMessage())); + // 打印信息到日志 + log.info(s.toString(), logininforEvent.getArgs()); + // 获取客户端操作系统 + String os = userAgent.getOs().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(logininforEvent.getUsername()); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(logininforEvent.getMessage()); + // 日志状态 + if (StringUtils.equalsAny(logininforEvent.getStatus(), Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { + logininfor.setStatus(Constants.SUCCESS); + } else if (Constants.LOGIN_FAIL.equals(logininforEvent.getStatus())) { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + insertLogininfor(logininfor); + } + + private String getBlock(Object msg) { + if (msg == null) { + msg = ""; + } + return "[" + msg.toString() + "]"; + } + + @Override + public TableDataInfo selectPageLogininforList(SysLogininfor logininfor, PageQuery pageQuery) { + Map params = logininfor.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) + .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) + .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime")); + if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { + pageQuery.setOrderByColumn("info_id"); + pageQuery.setIsAsc("desc"); + } + Page page = baseMapper.selectPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) { + logininfor.setLoginTime(new Date()); + baseMapper.insert(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininfor logininfor) { + Map params = logininfor.getParams(); + return baseMapper.selectList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) + .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) + .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime")) + .orderByDesc(SysLogininfor::getInfoId)); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) { + return baseMapper.deleteBatchIds(Arrays.asList(infoIds)); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() { + baseMapper.delete(new LambdaQueryWrapper<>()); + } +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysMenuServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..4bde1d7 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,446 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.rzdata.common.constant.Constants; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.entity.SysMenu; +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.TreeBuildUtils; +import com.rzdata.system.domain.SysRoleMenu; +import com.rzdata.system.domain.vo.MetaVo; +import com.rzdata.system.domain.vo.RouterVo; +import com.rzdata.system.mapper.SysMenuMapper; +import com.rzdata.system.mapper.SysRoleMapper; +import com.rzdata.system.mapper.SysRoleMenuMapper; +import com.rzdata.system.service.ISysMenuService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 菜单 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysMenuServiceImpl implements ISysMenuService { + + private final SysMenuMapper baseMapper; + private final SysRoleMapper roleMapper; + private final SysRoleMenuMapper roleMenuMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenu menu, Long userId) { + List menuList = null; + // 管理员显示所有菜单信息 + if (LoginHelper.isAdmin(userId)) { + menuList = baseMapper.selectList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()) + .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible()) + .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus()) + .orderByAsc(SysMenu::getParentId) + .orderByAsc(SysMenu::getOrderNum)); + } else { + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("sur.user_id", userId) + .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) + .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) + .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) + .orderByAsc("m.parent_id") + .orderByAsc("m.order_num"); + menuList = baseMapper.selectMenuListByUserId(wrapper); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) { + List perms = baseMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) { + List perms = baseMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) { + List menus = null; + if (LoginHelper.isAdmin(userId)) { + menus = baseMapper.selectMenuTreeAll(); + } else { + menus = baseMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) { + SysRole role = roleMapper.selectById(roleId); + return baseMapper.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List buildMenus(List menus) { + List routers = new LinkedList(); + for (SysMenu menu : menus) { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQueryParam()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (!cMenus.isEmpty() && UserConstants.TYPE_DIR.equals(menu.getMenuType())) { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } else if (isMenuFrame(menu)) { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(StringUtils.capitalize(menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQueryParam()); + childrenList.add(children); + router.setChildren(childrenList); + } else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(StringUtils.capitalize(routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List> buildMenuTreeSelect(List menus) { + if (CollUtil.isEmpty(menus)) { + return CollUtil.newArrayList(); + } + return TreeBuildUtils.build(menus, (menu, tree) -> + tree.setId(menu.getMenuId()) + .setParentId(menu.getParentId()) + .setName(menu.getMenuName()) + .setWeight(menu.getOrderNum())); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) { + return baseMapper.selectById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) { + return baseMapper.exists(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId)); + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) { + return roleMenuMapper.exists(new LambdaQueryWrapper().eq(SysRoleMenu::getMenuId, menuId)); + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) { + return baseMapper.insert(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) { + return baseMapper.updateById(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) { + return baseMapper.deleteById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public String checkMenuNameUnique(SysMenu menu) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysMenu::getMenuName, menu.getMenuName()) + .eq(SysMenu::getParentId, menu.getParentId()) + .ne(ObjectUtil.isNotNull(menu.getMenuId()), SysMenu::getMenuId, menu.getMenuId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * @return 路由名称 + */ + public String getRouteName(SysMenu menu) { + String routerName = StringUtils.capitalize(menu.getPath()); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) { + routerName = StringUtils.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(SysMenu menu) { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) { + component = menu.getComponent(); + } else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) { + component = UserConstants.INNER_LINK; + } else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(SysMenu menu) { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isInnerLink(SysMenu menu) { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath()); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(SysMenu menu) { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List getChildPerms(List list, int parentId) { + List returnList = new ArrayList(); + for (SysMenu t : list) { + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list + * @param t + */ + private void recursionFn(List list, SysMenu t) { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysMenu t) { + return StreamUtils.filter(list, n -> n.getParentId().equals(t.getMenuId())); + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysMenu t) { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + */ + public String innerLinkReplaceEach(String path) { + return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, "."}, + new String[]{"", "", "", "/"}); + } +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysNoticeServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..66e7eed --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,106 @@ +package com.rzdata.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.system.domain.SysNotice; +import com.rzdata.system.mapper.SysNoticeMapper; +import com.rzdata.system.service.ISysNoticeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 公告 服务层实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysNoticeServiceImpl implements ISysNoticeService { + + private final SysNoticeMapper baseMapper; + + @Override + public TableDataInfo selectPageNoticeList(SysNotice notice, PageQuery pageQuery) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle()) + .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType()) + .like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy()); + Page page = baseMapper.selectPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) { + return baseMapper.selectById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNotice notice) { + return baseMapper.selectList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle()) + .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType()) + .like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy())); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) { + return baseMapper.insert(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) { + return baseMapper.updateById(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) { + return baseMapper.deleteById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) { + return baseMapper.deleteBatchIds(Arrays.asList(noticeIds)); + } +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysOperLogServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..fe3e2dd --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,141 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ArrayUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.event.OperLogEvent; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.ip.AddressUtils; +import com.rzdata.system.domain.SysOperLog; +import com.rzdata.system.mapper.SysOperLogMapper; +import com.rzdata.system.service.ISysOperLogService; +import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 操作日志 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysOperLogServiceImpl implements ISysOperLogService { + + private final SysOperLogMapper baseMapper; + + /** + * 操作日志记录 + * + * @param operLogEvent 操作日志事件 + */ + @Async + @EventListener + public void recordOper(OperLogEvent operLogEvent) { + SysOperLog operLog = BeanUtil.toBean(operLogEvent, SysOperLog.class); + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + insertOperlog(operLog); + } + + @Override + public TableDataInfo selectPageOperLogList(SysOperLog operLog, PageQuery pageQuery) { + Map params = operLog.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) + .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, + SysOperLog::getBusinessType, operLog.getBusinessType()) + .func(f -> { + if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) { + f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); + } + }) + .eq(operLog.getStatus() != null, + SysOperLog::getStatus, operLog.getStatus()) + .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime")); + if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { + pageQuery.setOrderByColumn("oper_id"); + pageQuery.setIsAsc("desc"); + } + Page page = baseMapper.selectPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) { + operLog.setOperTime(new Date()); + baseMapper.insert(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLog operLog) { + Map params = operLog.getParams(); + return baseMapper.selectList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) + .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, + SysOperLog::getBusinessType, operLog.getBusinessType()) + .func(f -> { + if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) { + f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); + } + }) + .eq(operLog.getStatus() != null && operLog.getStatus() > 0, + SysOperLog::getStatus, operLog.getStatus()) + .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime")) + .orderByDesc(SysOperLog::getOperId)); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) { + return baseMapper.deleteBatchIds(Arrays.asList(operIds)); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) { + return baseMapper.selectById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() { + baseMapper.delete(new LambdaQueryWrapper<>()); + } +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysPostServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..67fb932 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,184 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.system.domain.SysPost; +import com.rzdata.system.domain.SysUserPost; +import com.rzdata.system.mapper.SysPostMapper; +import com.rzdata.system.mapper.SysUserPostMapper; +import com.rzdata.system.service.ISysPostService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 岗位信息 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysPostServiceImpl implements ISysPostService { + + private final SysPostMapper baseMapper; + private final SysUserPostMapper userPostMapper; + + @Override + public TableDataInfo selectPagePostList(SysPost post, PageQuery pageQuery) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode()) + .eq(StringUtils.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus()) + .like(StringUtils.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName()); + Page page = baseMapper.selectPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPost post) { + return baseMapper.selectList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode()) + .eq(StringUtils.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus()) + .like(StringUtils.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName())); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() { + return baseMapper.selectList(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) { + return baseMapper.selectById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) { + return baseMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public String checkPostNameUnique(SysPost post) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysPost::getPostName, post.getPostName()) + .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public String checkPostCodeUnique(SysPost post) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysPost::getPostCode, post.getPostCode()) + .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public long countUserPostById(Long postId) { + return userPostMapper.selectCount(new LambdaQueryWrapper().eq(SysUserPost::getPostId, postId)); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) { + return baseMapper.deleteById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) { + for (Long postId : postIds) { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return baseMapper.deleteBatchIds(Arrays.asList(postIds)); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) { + return baseMapper.insert(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) { + return baseMapper.updateById(post); + } +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysRoleServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..63625f8 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,412 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.system.domain.SysRoleDept; +import com.rzdata.system.domain.SysRoleMenu; +import com.rzdata.system.domain.SysUserRole; +import com.rzdata.system.mapper.SysRoleDeptMapper; +import com.rzdata.system.mapper.SysRoleMapper; +import com.rzdata.system.mapper.SysRoleMenuMapper; +import com.rzdata.system.mapper.SysUserRoleMapper; +import com.rzdata.system.service.ISysRoleService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 角色 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysRoleServiceImpl implements ISysRoleService { + + private final SysRoleMapper baseMapper; + private final SysRoleMenuMapper roleMenuMapper; + private final SysUserRoleMapper userRoleMapper; + private final SysRoleDeptMapper roleDeptMapper; + + @Override + public TableDataInfo selectPageRoleList(SysRole role, PageQuery pageQuery) { + Page page = baseMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role)); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + public List selectRoleList(SysRole role) { + return baseMapper.selectRoleList(this.buildQueryWrapper(role)); + } + + private Wrapper buildQueryWrapper(SysRole role) { + Map params = role.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("r.del_flag", UserConstants.ROLE_NORMAL) + .eq(ObjectUtil.isNotNull(role.getRoleId()), "r.role_id", role.getRoleId()) + .like(StringUtils.isNotBlank(role.getRoleName()), "r.role_name", role.getRoleName()) + .eq(StringUtils.isNotBlank(role.getStatus()), "r.status", role.getStatus()) + .like(StringUtils.isNotBlank(role.getRoleKey()), "r.role_key", role.getRoleKey()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "r.create_time", params.get("beginTime"), params.get("endTime")) + .orderByAsc("r.role_sort"); + return wrapper; + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) { + List userRoles = baseMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) { + for (SysRole userRole : userRoles) { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) { + List perms = baseMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) { + if (ObjectUtil.isNotNull(perm)) { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() { + return this.selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) { + return baseMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById(Long roleId) { + return baseMapper.selectById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public String checkRoleNameUnique(SysRole role) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysRole::getRoleName, role.getRoleName()) + .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public String checkRoleKeyUnique(SysRole role) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysRole::getRoleKey, role.getRoleKey()) + .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRole role) { + if (ObjectUtil.isNotNull(role.getRoleId()) && role.isAdmin()) { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + @Override + public void checkRoleDataScope(Long roleId) { + if (!LoginHelper.isAdmin()) { + SysRole role = new SysRole(); + role.setRoleId(roleId); + List roles = this.selectRoleList(role); + if (CollUtil.isEmpty(roles)) { + throw new ServiceException("没有权限访问角色数据!"); + } + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public long countUserRoleByRoleId(Long roleId) { + return userRoleMapper.selectCount(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertRole(SysRole role) { + // 新增角色信息 + baseMapper.insert(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateRole(SysRole role) { + // 修改角色信息 + baseMapper.updateById(role); + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, role.getRoleId())); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public int updateRoleStatus(SysRole role) { + return baseMapper.updateById(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int authDataScope(SysRole role) { + // 修改角色信息 + baseMapper.updateById(role); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, role.getRoleId())); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long menuId : role.getMenuIds()) { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) { + rows = roleMenuMapper.insertBatch(list) ? list.size() : 0; + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList(); + for (Long deptId : role.getDeptIds()) { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) { + rows = roleDeptMapper.insertBatch(list) ? list.size() : 0; + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleById(Long roleId) { + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, roleId)); + return baseMapper.deleteById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleByIds(Long[] roleIds) { + for (Long roleId : roleIds) { + checkRoleAllowed(new SysRole(roleId)); + checkRoleDataScope(roleId); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + List ids = Arrays.asList(roleIds); + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, ids)); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().in(SysRoleDept::getRoleId, ids)); + return baseMapper.deleteBatchIds(ids); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) { + return userRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getRoleId, userRole.getRoleId()) + .eq(SysUserRole::getUserId, userRole.getUserId())); + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) { + return userRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getRoleId, roleId) + .in(SysUserRole::getUserId, Arrays.asList(userIds))); + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) { + // 新增用户与角色管理 + int rows = 1; + List list = new ArrayList(); + for (Long userId : userIds) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + if (list.size() > 0) { + rows = userRoleMapper.insertBatch(list) ? list.size() : 0; + } + return rows; + } +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysSensitiveServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysSensitiveServiceImpl.java new file mode 100644 index 0000000..aa11846 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysSensitiveServiceImpl.java @@ -0,0 +1,26 @@ +package com.rzdata.system.service.impl; + +import com.rzdata.common.core.service.SensitiveService; +import com.rzdata.common.helper.LoginHelper; +import org.springframework.stereotype.Service; + +/** + * 脱敏服务 + * 默认管理员不过滤 + * 需自行根据业务重写实现 + * + * @author Lion Li + * @version 3.6.0 + */ +@Service +public class SysSensitiveServiceImpl implements SensitiveService { + + /** + * 是否脱敏 + */ + @Override + public boolean isSensitive() { + return !LoginHelper.isAdmin(); + } + +} diff --git a/service/src/main/java/com/rzdata/system/service/impl/SysUserServiceImpl.java b/service/src/main/java/com/rzdata/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..ac878e2 --- /dev/null +++ b/service/src/main/java/com/rzdata/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,488 @@ +package com.rzdata.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.entity.SysDept; +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.exception.ServiceException; +import com.rzdata.common.helper.DataBaseHelper; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.system.domain.SysPost; +import com.rzdata.system.domain.SysUserPost; +import com.rzdata.system.domain.SysUserRole; +import com.rzdata.system.mapper.*; +import com.rzdata.system.service.ISysUserService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 用户 业务层处理 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysUserServiceImpl implements ISysUserService { + + private final SysUserMapper baseMapper; + private final SysDeptMapper deptMapper; + private final SysRoleMapper roleMapper; + private final SysPostMapper postMapper; + private final SysUserRoleMapper userRoleMapper; + private final SysUserPostMapper userPostMapper; + + @Override + public TableDataInfo selectPageUserList(SysUser user, PageQuery pageQuery) { + Page page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user)); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public List selectUserList(SysUser user) { + return baseMapper.selectUserList(this.buildQueryWrapper(user)); + } + + private Wrapper buildQueryWrapper(SysUser user) { + Map params = user.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) + .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "u.create_time", params.get("beginTime"), params.get("endTime")) + .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { + List deptList = deptMapper.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(user.getDeptId()); + w.in("u.dept_id", ids); + }); + return wrapper; + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public TableDataInfo selectAllocatedList(SysUser user, PageQuery pageQuery) { + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) + .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()); + Page page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public TableDataInfo selectUnallocatedList(SysUser user, PageQuery pageQuery) { + List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) + .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) + .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()); + Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper); + return TableDataInfo.build(page); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) { + return baseMapper.selectUserByUserName(userName); + } + + /** + * 通过手机号查询用户 + * + * @param phonenumber 手机号 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByPhonenumber(String phonenumber) { + return baseMapper.selectUserByPhonenumber(phonenumber); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) { + return baseMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) { + List list = roleMapper.selectRolesByUserName(userName); + if (CollUtil.isEmpty(list)) { + return StringUtils.EMPTY; + } + return StreamUtils.join(list, SysRole::getRoleName); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) { + List list = postMapper.selectPostsByUserName(userName); + if (CollUtil.isEmpty(list)) { + return StringUtils.EMPTY; + } + return StreamUtils.join(list, SysPost::getPostName); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public String checkUserNameUnique(SysUser user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getUserName, user.getUserName()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkPhoneUnique(SysUser user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getPhonenumber, user.getPhonenumber()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkEmailUnique(SysUser user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getEmail, user.getEmail()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + if (exist) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) { + if (ObjectUtil.isNotNull(user.getUserId()) && user.isAdmin()) { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) { + if (!LoginHelper.isAdmin()) { + SysUser user = new SysUser(); + user.setUserId(userId); + List users = this.selectUserList(user); + if (CollUtil.isEmpty(users)) { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertUser(SysUser user) { + // 新增用户信息 + int rows = baseMapper.insert(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean registerUser(SysUser user) { + user.setCreateBy(user.getUserName()); + user.setUpdateBy(user.getUserName()); + return baseMapper.insert(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateUser(SysUser user) { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, userId)); + // 新增用户与岗位管理 + insertUserPost(user); + return baseMapper.updateById(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void insertUserAuth(Long userId, Long[] roleIds) { + userRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getUserId, userId)); + insertUserRole(userId, roleIds); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) { + return baseMapper.updateById(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) { + return baseMapper.updateById(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getAvatar, avatar) + .eq(SysUser::getUserName, userName)) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) { + return baseMapper.updateById(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getPassword, password) + .eq(SysUser::getUserName, userName)); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) { + this.insertUserRole(user.getUserId(), user.getRoleIds()); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) { + Long[] posts = user.getPostIds(); + if (ArrayUtil.isNotEmpty(posts)) { + // 新增用户与岗位管理 + List list = new ArrayList<>(posts.length); + for (Long postId : posts) { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + userPostMapper.insertBatch(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserRole(Long userId, Long[] roleIds) { + if (ArrayUtil.isNotEmpty(roleIds)) { + // 新增用户与角色管理 + List list = new ArrayList<>(roleIds.length); + for (Long roleId : roleIds) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.insertBatch(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserById(Long userId) { + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); + // 删除用户与岗位表 + userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, userId)); + return baseMapper.deleteById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserByIds(Long[] userIds) { + for (Long userId : userIds) { + checkUserAllowed(new SysUser(userId)); + checkUserDataScope(userId); + } + List ids = Arrays.asList(userIds); + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().in(SysUserRole::getUserId, ids)); + // 删除用户与岗位表 + userPostMapper.delete(new LambdaQueryWrapper().in(SysUserPost::getUserId, ids)); + return baseMapper.deleteBatchIds(ids); + } + +} diff --git a/service/src/main/java/com/rzdata/web/controller/common/CaptchaController.java b/service/src/main/java/com/rzdata/web/controller/common/CaptchaController.java new file mode 100644 index 0000000..90ea4af --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/common/CaptchaController.java @@ -0,0 +1,112 @@ +package com.rzdata.web.controller.common; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.captcha.AbstractCaptcha; +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.constant.Constants; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.enums.CaptchaType; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.RedisUtils; +import com.rzdata.common.utils.reflect.ReflectUtils; +import com.rzdata.common.utils.spring.SpringUtils; +import com.rzdata.framework.config.properties.CaptchaProperties; +import com.rzdata.sms.config.properties.SmsProperties; +import com.rzdata.sms.core.SmsTemplate; +import com.rzdata.sms.entity.SmsResult; +import com.rzdata.system.service.ISysConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotBlank; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +/** + * 验证码操作处理 + * + * @author Lion Li + */ +@SaIgnore +@Slf4j +@Validated +@RequiredArgsConstructor +@RestController +public class CaptchaController { + + private final CaptchaProperties captchaProperties; + private final SmsProperties smsProperties; + private final ISysConfigService configService; + + /** + * 短信验证码 + * + * @param phonenumber 用户手机号 + */ + @GetMapping("/captchaSms") + public R smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") + String phonenumber) { + if (!smsProperties.getEnabled()) { + return R.fail("当前系统没有开启短信功能!"); + } + String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber; + String code = RandomUtil.randomNumbers(4); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + // 验证码模板id 自行处理 (查数据库或写死均可) + String templateId = ""; + Map map = new HashMap<>(1); + map.put("code", code); + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + SmsResult result = smsTemplate.send(phonenumber, templateId, map); + if (!result.isSuccess()) { + log.error("验证码短信发送异常 => {}", result); + return R.fail(result.getMessage()); + } + return R.ok(); + } + + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public R> getCode() { + Map ajax = new HashMap<>(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) { + return R.ok(ajax); + } + // 保存验证码信息 + String uuid = IdUtil.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + // 生成验证码 + CaptchaType captchaType = captchaProperties.getType(); + boolean isMath = CaptchaType.MATH == captchaType; + Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength(); + CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length); + AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); + captcha.setGenerator(codeGenerator); + captcha.createCode(); + String code = captcha.getCode(); + if (isMath) { + ExpressionParser parser = new SpelExpressionParser(); + Expression exp = parser.parseExpression(StringUtils.remove(code, "=")); + code = exp.getValue(String.class); + } + RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + ajax.put("uuid", uuid); + ajax.put("img", captcha.getImageBase64()); + return R.ok(ajax); + } + +} diff --git a/service/src/main/java/com/rzdata/web/controller/monitor/CacheController.java b/service/src/main/java/com/rzdata/web/controller/monitor/CacheController.java new file mode 100644 index 0000000..8417b38 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/monitor/CacheController.java @@ -0,0 +1,169 @@ +package com.rzdata.web.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.collection.CollUtil; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.constant.CacheNames; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.utils.JsonUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.CacheUtils; +import com.rzdata.common.utils.redis.RedisUtils; +import com.rzdata.system.domain.SysCache; +import lombok.RequiredArgsConstructor; +import org.redisson.spring.data.connection.RedissonConnectionFactory; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 缓存监控 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/cache") +public class CacheController { + + private final RedissonConnectionFactory connectionFactory; + + private final static List CACHES = new ArrayList<>(); + + static { + CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息")); + CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户")); + CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息")); + CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典")); + CACHES.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码")); + CACHES.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交")); + CACHES.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理")); + CACHES.add(new SysCache(CacheNames.SYS_OSS_CONFIG, "OSS配置")); + CACHES.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); + } + + /** + * 获取缓存监控列表 + */ + @SaCheckPermission("monitor:cache:list") + @GetMapping() + public R> getInfo() throws Exception { + RedisConnection connection = connectionFactory.getConnection(); + Properties info = connection.info(); + Properties commandStats = connection.info("commandstats"); + Long dbSize = connection.dbSize(); + + Map result = new HashMap<>(3); + result.put("info", info); + result.put("dbSize", dbSize); + + List> pieList = new ArrayList<>(); + if (commandStats != null) { + commandStats.stringPropertyNames().forEach(key -> { + Map data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StringUtils.removeStart(key, "cmdstat_")); + data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); + pieList.add(data); + }); + } + result.put("commandStats", pieList); + return R.ok(result); + } + + /** + * 获取缓存监控缓存名列表 + */ + @SaCheckPermission("monitor:cache:list") + @GetMapping("/getNames") + public R> cache() { + return R.ok(CACHES); + } + + /** + * 获取缓存监控Key列表 + * + * @param cacheName 缓存名 + */ + @SaCheckPermission("monitor:cache:list") + @GetMapping("/getKeys/{cacheName}") + public R> getCacheKeys(@PathVariable String cacheName) { + Collection cacheKeys = new HashSet<>(0); + if (isCacheNames(cacheName)) { + Set keys = CacheUtils.keys(cacheName); + if (CollUtil.isNotEmpty(keys)) { + cacheKeys = keys.stream().map(Object::toString).collect(Collectors.toList()); + } + } else { + cacheKeys = RedisUtils.keys(cacheName + "*"); + } + return R.ok(cacheKeys); + } + + /** + * 获取缓存监控缓存值详情 + * + * @param cacheName 缓存名 + * @param cacheKey 缓存key + */ + @SaCheckPermission("monitor:cache:list") + @GetMapping("/getValue/{cacheName}/{cacheKey}") + public R getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) { + Object cacheValue; + if (isCacheNames(cacheName)) { + cacheValue = CacheUtils.get(cacheName, cacheKey); + } else { + cacheValue = RedisUtils.getCacheObject(cacheKey); + } + SysCache sysCache = new SysCache(cacheName, cacheKey, JsonUtils.toJsonString(cacheValue)); + return R.ok(sysCache); + } + + /** + * 清理缓存监控缓存名 + * + * @param cacheName 缓存名 + */ + @SaCheckPermission("monitor:cache:list") + @DeleteMapping("/clearCacheName/{cacheName}") + public R clearCacheName(@PathVariable String cacheName) { + if (isCacheNames(cacheName)) { + CacheUtils.clear(cacheName); + } else { + RedisUtils.deleteKeys(cacheName + "*"); + } + return R.ok(); + } + + /** + * 清理缓存监控Key + * + * @param cacheKey key名 + */ + @SaCheckPermission("monitor:cache:list") + @DeleteMapping("/clearCacheKey/{cacheName}/{cacheKey}") + public R clearCacheKey(@PathVariable String cacheName, @PathVariable String cacheKey) { + if (isCacheNames(cacheName)) { + CacheUtils.evict(cacheName, cacheKey); + } else { + RedisUtils.deleteObject(cacheKey); + } + return R.ok(); + } + + /** + * 清理全部缓存监控 + */ + @SaCheckPermission("monitor:cache:list") + @DeleteMapping("/clearCacheAll") + public R clearCacheAll() { + RedisUtils.deleteKeys("*"); + return R.ok(); + } + + private boolean isCacheNames(String cacheName) { + return !StringUtils.contains(cacheName, ":"); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/monitor/SysLogininforController.java b/service/src/main/java/com/rzdata/web/controller/monitor/SysLogininforController.java new file mode 100644 index 0000000..c10f849 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,88 @@ +package com.rzdata.web.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.common.utils.redis.RedisUtils; +import com.rzdata.system.domain.SysLogininfor; +import com.rzdata.system.service.ISysLogininforService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 系统访问记录 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController { + + private final ISysLogininforService logininforService; + + /** + * 获取系统访问记录列表 + */ + @SaCheckPermission("monitor:logininfor:list") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor, PageQuery pageQuery) { + return logininforService.selectPageLogininforList(logininfor, pageQuery); + } + + /** + * 导出系统访问记录列表 + */ + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @SaCheckPermission("monitor:logininfor:export") + @PostMapping("/export") + public void export(SysLogininfor logininfor, HttpServletResponse response) { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil.exportExcel(list, "登录日志", SysLogininfor.class, response); + } + + /** + * 批量删除登录日志 + * @param infoIds 日志ids + */ + @SaCheckPermission("monitor:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public R remove(@PathVariable Long[] infoIds) { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + /** + * 清理系统访问记录 + */ + @SaCheckPermission("monitor:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public R clean() { + logininforService.cleanLogininfor(); + return R.ok(); + } + + @SaCheckPermission("monitor:logininfor:unlock") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public R unlock(@PathVariable("userName") String userName) { + String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName; + if (RedisUtils.hasKey(loginName)) { + RedisUtils.deleteObject(loginName); + } + return R.ok(); + } + +} diff --git a/service/src/main/java/com/rzdata/web/controller/monitor/SysOperlogController.java b/service/src/main/java/com/rzdata/web/controller/monitor/SysOperlogController.java new file mode 100644 index 0000000..5a6b755 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,74 @@ +package com.rzdata.web.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.system.domain.SysOperLog; +import com.rzdata.system.service.ISysOperLogService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 操作日志记录 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController { + + private final ISysOperLogService operLogService; + + /** + * 获取操作日志记录列表 + */ + @SaCheckPermission("monitor:operlog:list") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog, PageQuery pageQuery) { + return operLogService.selectPageOperLogList(operLog, pageQuery); + } + + /** + * 导出操作日志记录列表 + */ + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @SaCheckPermission("monitor:operlog:export") + @PostMapping("/export") + public void export(SysOperLog operLog, HttpServletResponse response) { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil.exportExcel(list, "操作日志", SysOperLog.class, response); + } + + /** + * 批量删除操作日志记录 + * @param operIds 日志ids + */ + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @SaCheckPermission("monitor:operlog:remove") + @DeleteMapping("/{operIds}") + public R remove(@PathVariable Long[] operIds) { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + /** + * 清理操作日志记录 + */ + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @SaCheckPermission("monitor:operlog:remove") + @DeleteMapping("/clean") + public R clean() { + operLogService.cleanOperLog(); + return R.ok(); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/monitor/SysUserOnlineController.java b/service/src/main/java/com/rzdata/web/controller/monitor/SysUserOnlineController.java new file mode 100644 index 0000000..1e2905c --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,90 @@ +package com.rzdata.web.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.CacheConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.dto.UserOnlineDTO; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.redis.RedisUtils; +import com.rzdata.system.domain.SysUserOnline; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 在线用户监控 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController { + + /** + * 获取在线用户监控列表 + * + * @param ipaddr IP地址 + * @param userName 用户名 + */ + @SaCheckPermission("monitor:online:list") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) { + // 获取所有未过期的 token + List keys = StpUtil.searchTokenValue("", 0, -1, false); + List userOnlineDTOList = new ArrayList<>(); + for (String key : keys) { + String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, ""); + // 如果已经过期则跳过 + if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) { + continue; + } + userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)); + } + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(ipaddr, userOnline.getIpaddr()) && + StringUtils.equals(userName, userOnline.getUserName()) + ); + } else if (StringUtils.isNotEmpty(ipaddr)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(ipaddr, userOnline.getIpaddr()) + ); + } else if (StringUtils.isNotEmpty(userName)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(userName, userOnline.getUserName()) + ); + } + Collections.reverse(userOnlineDTOList); + userOnlineDTOList.removeAll(Collections.singleton(null)); + List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); + return TableDataInfo.build(userOnlineList); + } + + /** + * 强退用户 + * + * @param tokenId token值 + */ + @SaCheckPermission("monitor:online:forceLogout") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public R forceLogout(@PathVariable String tokenId) { + try { + StpUtil.kickoutByTokenValue(tokenId); + } catch (NotLoginException ignored) { + } + return R.ok(); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysConfigController.java b/service/src/main/java/com/rzdata/web/controller/system/SysConfigController.java new file mode 100644 index 0000000..1c08401 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysConfigController.java @@ -0,0 +1,137 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.system.domain.SysConfig; +import com.rzdata.system.service.ISysConfigService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 参数配置 信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController { + + private final ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @SaCheckPermission("system:config:list") + @GetMapping("/list") + public TableDataInfo list(SysConfig config, PageQuery pageQuery) { + return configService.selectPageConfigList(config, pageQuery); + } + + /** + * 导出参数配置列表 + */ + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:config:export") + @PostMapping("/export") + public void export(SysConfig config, HttpServletResponse response) { + List list = configService.selectConfigList(config); + ExcelUtil.exportExcel(list, "参数数据", SysConfig.class, response); + } + + /** + * 根据参数编号获取详细信息 + * + * @param configId 参数ID + */ + @SaCheckPermission("system:config:query") + @GetMapping(value = "/{configId}") + public R getInfo(@PathVariable Long configId) { + return R.ok(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + * + * @param configKey 参数Key + */ + @GetMapping(value = "/configKey/{configKey}") + public R getConfigKey(@PathVariable String configKey) { + return R.ok(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @SaCheckPermission("system:config:add") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysConfig config) { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) { + return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + configService.insertConfig(config); + return R.ok(); + } + + /** + * 修改参数配置 + */ + @SaCheckPermission("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysConfig config) { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) { + return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + configService.updateConfig(config); + return R.ok(); + } + + /** + * 根据参数键名修改参数配置 + */ + @SaCheckPermission("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping("/updateByKey") + public R updateByKey(@RequestBody SysConfig config) { + configService.updateConfig(config); + return R.ok(); + } + + /** + * 删除参数配置 + * + * @param configIds 参数ID串 + */ + @SaCheckPermission("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public R remove(@PathVariable Long[] configIds) { + configService.deleteConfigByIds(configIds); + return R.ok(); + } + + /** + * 刷新参数缓存 + */ + @SaCheckPermission("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public R refreshCache() { + configService.resetConfigCache(); + return R.ok(); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysDeptController.java b/service/src/main/java/com/rzdata/web/controller/system/SysDeptController.java new file mode 100644 index 0000000..7316f5c --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysDeptController.java @@ -0,0 +1,119 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ArrayUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.entity.SysDept; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.system.service.ISysDeptService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 部门信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController { + + private final ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @SaCheckPermission("system:dept:list") + @GetMapping("/list") + public R> list(SysDept dept) { + List depts = deptService.selectDeptList(dept); + return R.ok(depts); + } + + /** + * 查询部门列表(排除节点) + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:list") + @GetMapping("/list/exclude/{deptId}") + public R> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) { + List depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().equals(deptId) + || ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return R.ok(depts); + } + + /** + * 根据部门编号获取详细信息 + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:query") + @GetMapping(value = "/{deptId}") + public R getInfo(@PathVariable Long deptId) { + deptService.checkDeptDataScope(deptId); + return R.ok(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @SaCheckPermission("system:dept:add") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDept dept) { + if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) { + return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @SaCheckPermission("system:dept:edit") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDept dept) { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) { + return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } else if (dept.getParentId().equals(deptId)) { + return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) + && deptService.selectNormalChildrenDeptById(deptId) > 0) { + return R.fail("该部门包含未停用的子部门!"); + } + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:remove") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public R remove(@PathVariable Long deptId) { + if (deptService.hasChildByDeptId(deptId)) { + return R.warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) { + return R.warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysDictDataController.java b/service/src/main/java/com/rzdata/web/controller/system/SysDictDataController.java new file mode 100644 index 0000000..964fa70 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysDictDataController.java @@ -0,0 +1,116 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ObjectUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.entity.SysDictData; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.system.service.ISysDictDataService; +import com.rzdata.system.service.ISysDictTypeService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +/** + * 数据字典信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController { + + private final ISysDictDataService dictDataService; + private final ISysDictTypeService dictTypeService; + + /** + * 查询字典数据列表 + */ + @SaCheckPermission("system:dict:list") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData, PageQuery pageQuery) { + return dictDataService.selectPageDictDataList(dictData, pageQuery); + } + + /** + * 导出字典数据列表 + */ + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:dict:export") + @PostMapping("/export") + public void export(SysDictData dictData, HttpServletResponse response) { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil.exportExcel(list, "字典数据", SysDictData.class, response); + } + + /** + * 查询字典数据详细 + * + * @param dictCode 字典code + */ + @SaCheckPermission("system:dict:query") + @GetMapping(value = "/{dictCode}") + public R getInfo(@PathVariable Long dictCode) { + return R.ok(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + * + * @param dictType 字典类型 + */ + @GetMapping(value = "/type/{dictType}") + public R> dictType(@PathVariable String dictType) { + List data = dictTypeService.selectDictDataByType(dictType); + if (ObjectUtil.isNull(data)) { + data = new ArrayList<>(); + } + return R.ok(data); + } + + /** + * 新增字典类型 + */ + @SaCheckPermission("system:dict:add") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDictData dict) { + dictDataService.insertDictData(dict); + return R.ok(); + } + + /** + * 修改保存字典类型 + */ + @SaCheckPermission("system:dict:edit") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDictData dict) { + dictDataService.updateDictData(dict); + return R.ok(); + } + + /** + * 删除字典类型 + * + * @param dictCodes 字典code串 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public R remove(@PathVariable Long[] dictCodes) { + dictDataService.deleteDictDataByIds(dictCodes); + return R.ok(); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysDictTypeController.java b/service/src/main/java/com/rzdata/web/controller/system/SysDictTypeController.java new file mode 100644 index 0000000..2afe011 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysDictTypeController.java @@ -0,0 +1,125 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.entity.SysDictType; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.system.service.ISysDictTypeService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 数据字典信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController { + + private final ISysDictTypeService dictTypeService; + + /** + * 查询字典类型列表 + */ + @SaCheckPermission("system:dict:list") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType, PageQuery pageQuery) { + return dictTypeService.selectPageDictTypeList(dictType, pageQuery); + } + + /** + * 导出字典类型列表 + */ + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:dict:export") + @PostMapping("/export") + public void export(SysDictType dictType, HttpServletResponse response) { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response); + } + + /** + * 查询字典类型详细 + * + * @param dictId 字典ID + */ + @SaCheckPermission("system:dict:query") + @GetMapping(value = "/{dictId}") + public R getInfo(@PathVariable Long dictId) { + return R.ok(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @SaCheckPermission("system:dict:add") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDictType dict) { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) { + return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dictTypeService.insertDictType(dict); + return R.ok(); + } + + /** + * 修改字典类型 + */ + @SaCheckPermission("system:dict:edit") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDictType dict) { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) { + return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dictTypeService.updateDictType(dict); + return R.ok(); + } + + /** + * 删除字典类型 + * + * @param dictIds 字典ID串 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public R remove(@PathVariable Long[] dictIds) { + dictTypeService.deleteDictTypeByIds(dictIds); + return R.ok(); + } + + /** + * 刷新字典缓存 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public R refreshCache() { + dictTypeService.resetDictCache(); + return R.ok(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public R> optionselect() { + List dictTypes = dictTypeService.selectDictTypeAll(); + return R.ok(dictTypes); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysIndexController.java b/service/src/main/java/com/rzdata/web/controller/system/SysIndexController.java new file mode 100644 index 0000000..8bc8a22 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysIndexController.java @@ -0,0 +1,32 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.rzdata.common.config.RuoYiConfig; +import com.rzdata.common.utils.StringUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 首页 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +public class SysIndexController { + + /** + * 系统基础配置 + */ + private final RuoYiConfig ruoyiConfig; + + /** + * 访问首页,提示语 + */ + @SaIgnore + @GetMapping("/") + public String index() { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysLoginController.java b/service/src/main/java/com/rzdata/web/controller/system/SysLoginController.java new file mode 100644 index 0000000..b5f23e1 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysLoginController.java @@ -0,0 +1,128 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.rzdata.common.constant.Constants; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.entity.SysMenu; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.core.domain.model.LoginBody; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.core.domain.model.SmsLoginBody; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.system.domain.vo.RouterVo; +import com.rzdata.system.service.ISysMenuService; +import com.rzdata.system.service.ISysUserService; +import com.rzdata.system.service.SysLoginService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotBlank; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 登录验证 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +public class SysLoginController { + + private final SysLoginService loginService; + private final ISysMenuService menuService; + private final ISysUserService userService; + + /** + * 登录方法 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @SaIgnore + @PostMapping("/login") + public R> login(@Validated @RequestBody LoginBody loginBody) { + Map ajax = new HashMap<>(); + // 生成令牌 + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), + loginBody.getUuid()); + ajax.put(Constants.TOKEN, token); + return R.ok(ajax); + } + + /** + * 短信登录(示例) + * + * @param smsLoginBody 登录信息 + * @return 结果 + */ + @SaIgnore + @PostMapping("/smsLogin") + public R> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) { + Map ajax = new HashMap<>(); + // 生成令牌 + String token = loginService.smsLogin(smsLoginBody.getPhonenumber(), smsLoginBody.getSmsCode()); + ajax.put(Constants.TOKEN, token); + return R.ok(ajax); + } + + /** + * 小程序登录(示例) + * + * @param xcxCode 小程序code + * @return 结果 + */ + @SaIgnore + @PostMapping("/xcxLogin") + public R> xcxLogin(@NotBlank(message = "{xcx.code.not.blank}") String xcxCode) { + Map ajax = new HashMap<>(); + // 生成令牌 + String token = loginService.xcxLogin(xcxCode); + ajax.put(Constants.TOKEN, token); + return R.ok(ajax); + } + + /** + * 退出登录 + */ + @SaIgnore + @PostMapping("/logout") + public R logout() { + loginService.logout(); + return R.ok("退出成功"); + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public R> getInfo() { + LoginUser loginUser = LoginHelper.getLoginUser(); + SysUser user = userService.selectUserById(loginUser.getUserId()); + Map ajax = new HashMap<>(); + ajax.put("user", user); + ajax.put("roles", loginUser.getRolePermission()); + ajax.put("permissions", loginUser.getMenuPermission()); + return R.ok(ajax); + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public R> getRouters() { + Long userId = LoginHelper.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return R.ok(menuService.buildMenus(menus)); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysMenuController.java b/service/src/main/java/com/rzdata/web/controller/system/SysMenuController.java new file mode 100644 index 0000000..58be850 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysMenuController.java @@ -0,0 +1,127 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.lang.tree.Tree; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.entity.SysMenu; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.system.service.ISysMenuService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 菜单信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController { + + private final ISysMenuService menuService; + + /** + * 获取菜单列表 + */ + @SaCheckPermission("system:menu:list") + @GetMapping("/list") + public R> list(SysMenu menu) { + List menus = menuService.selectMenuList(menu, getUserId()); + return R.ok(menus); + } + + /** + * 根据菜单编号获取详细信息 + * + * @param menuId 菜单ID + */ + @SaCheckPermission("system:menu:query") + @GetMapping(value = "/{menuId}") + public R getInfo(@PathVariable Long menuId) { + return R.ok(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public R>> treeselect(SysMenu menu) { + List menus = menuService.selectMenuList(menu, getUserId()); + return R.ok(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + * + * @param roleId 角色ID + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public R> roleMenuTreeselect(@PathVariable("roleId") Long roleId) { + List menus = menuService.selectMenuList(getUserId()); + Map ajax = new HashMap<>(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return R.ok(ajax); + } + + /** + * 新增菜单 + */ + @SaCheckPermission("system:menu:add") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysMenu menu) { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) { + return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @SaCheckPermission("system:menu:edit") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysMenu menu) { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } else if (menu.getMenuId().equals(menu.getParentId())) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + * + * @param menuId 菜单ID + */ + @SaCheckPermission("system:menu:remove") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public R remove(@PathVariable("menuId") Long menuId) { + if (menuService.hasChildByMenuId(menuId)) { + return R.warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) { + return R.warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysNoticeController.java b/service/src/main/java/com/rzdata/web/controller/system/SysNoticeController.java new file mode 100644 index 0000000..361149d --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysNoticeController.java @@ -0,0 +1,80 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.system.domain.SysNotice; +import com.rzdata.system.service.ISysNoticeService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 公告 信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController { + + private final ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @SaCheckPermission("system:notice:list") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice, PageQuery pageQuery) { + return noticeService.selectPageNoticeList(notice, pageQuery); + } + + /** + * 根据通知公告编号获取详细信息 + * + * @param noticeId 公告ID + */ + @SaCheckPermission("system:notice:query") + @GetMapping(value = "/{noticeId}") + public R getInfo(@PathVariable Long noticeId) { + return R.ok(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @SaCheckPermission("system:notice:add") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysNotice notice) { + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @SaCheckPermission("system:notice:edit") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysNotice notice) { + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + * + * @param noticeIds 公告ID串 + */ + @SaCheckPermission("system:notice:remove") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public R remove(@PathVariable Long[] noticeIds) { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysPostController.java b/service/src/main/java/com/rzdata/web/controller/system/SysPostController.java new file mode 100644 index 0000000..4a664ed --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysPostController.java @@ -0,0 +1,115 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.system.domain.SysPost; +import com.rzdata.system.service.ISysPostService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 岗位信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController { + + private final ISysPostService postService; + + /** + * 获取岗位列表 + */ + @SaCheckPermission("system:post:list") + @GetMapping("/list") + public TableDataInfo list(SysPost post, PageQuery pageQuery) { + return postService.selectPagePostList(post, pageQuery); + } + + /** + * 导出岗位列表 + */ + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:post:export") + @PostMapping("/export") + public void export(SysPost post, HttpServletResponse response) { + List list = postService.selectPostList(post); + ExcelUtil.exportExcel(list, "岗位数据", SysPost.class, response); + } + + /** + * 根据岗位编号获取详细信息 + * + * @param postId 岗位ID + */ + @SaCheckPermission("system:post:query") + @GetMapping(value = "/{postId}") + public R getInfo(@PathVariable Long postId) { + return R.ok(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @SaCheckPermission("system:post:add") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysPost post) { + if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) { + return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) { + return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @SaCheckPermission("system:post:edit") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysPost post) { + if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) { + return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) { + return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + * + * @param postIds 岗位ID串 + */ + @SaCheckPermission("system:post:remove") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public R remove(@PathVariable Long[] postIds) { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public R> optionselect() { + List posts = postService.selectPostAll(); + return R.ok(posts); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysProfileController.java b/service/src/main/java/com/rzdata/web/controller/system/SysProfileController.java new file mode 100644 index 0000000..3797835 --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysProfileController.java @@ -0,0 +1,124 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.core.io.FileUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.file.MimeTypeUtils; +import com.rzdata.system.service.ISysUserService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * 个人信息 业务处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController { + + private final ISysUserService userService; + + /** + * 个人信息 + */ + @GetMapping + public R> profile() { + SysUser user = userService.selectUserById(getUserId()); + Map ajax = new HashMap<>(); + ajax.put("user", user); + ajax.put("roleGroup", userService.selectUserRoleGroup(user.getUserName())); + ajax.put("postGroup", userService.selectUserPostGroup(user.getUserName())); + return R.ok(ajax); + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public R updateProfile(@RequestBody SysUser user) { + if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUserId(getUserId()); + user.setUserName(null); + user.setPassword(null); + user.setAvatar(null); + user.setDeptId(null); + if (userService.updateUserProfile(user) > 0) { + return R.ok(); + } + return R.fail("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + * + * @param newPassword 旧密码 + * @param oldPassword 新密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public R updatePwd(String oldPassword, String newPassword) { + SysUser user = userService.selectUserById(LoginHelper.getUserId()); + String userName = user.getUserName(); + String password = user.getPassword(); + if (!BCrypt.checkpw(oldPassword, password)) { + return R.fail("修改密码失败,旧密码错误"); + } + if (BCrypt.checkpw(newPassword, password)) { + return R.fail("新密码不能与旧密码相同"); + } + + if (userService.resetUserPwd(userName, BCrypt.hashpw(newPassword)) > 0) { + return R.ok(); + } + return R.fail("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + * + * @param avatarfile 用户头像 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) { + Map ajax = new HashMap<>(); + if (!avatarfile.isEmpty()) { + String extension = FileUtil.extName(avatarfile.getOriginalFilename()); + if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { + return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); + } + + String avatar = ""; + if (userService.updateUserAvatar(getUsername(), avatar)) { + ajax.put("imgUrl", avatar); + return R.ok(ajax); + } + } + return R.fail("上传图片异常,请联系管理员"); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysRegisterController.java b/service/src/main/java/com/rzdata/web/controller/system/SysRegisterController.java new file mode 100644 index 0000000..d4e50df --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysRegisterController.java @@ -0,0 +1,40 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.model.RegisterBody; +import com.rzdata.system.service.ISysConfigService; +import com.rzdata.system.service.SysRegisterService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * 注册验证 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +public class SysRegisterController extends BaseController { + + private final SysRegisterService registerService; + private final ISysConfigService configService; + + /** + * 用户注册 + */ + @SaIgnore + @PostMapping("/register") + public R register(@Validated @RequestBody RegisterBody user) { + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) { + return R.fail("当前系统没有开启注册功能!"); + } + registerService.register(user); + return R.ok(); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysRoleController.java b/service/src/main/java/com/rzdata/web/controller/system/SysRoleController.java new file mode 100644 index 0000000..bd6ab9a --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysRoleController.java @@ -0,0 +1,237 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ObjectUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.entity.SysDept; +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.core.domain.model.LoginUser; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.system.domain.SysUserRole; +import com.rzdata.system.service.ISysDeptService; +import com.rzdata.system.service.ISysRoleService; +import com.rzdata.system.service.ISysUserService; +import com.rzdata.system.service.SysPermissionService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 角色信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController { + + private final ISysRoleService roleService; + private final ISysUserService userService; + private final ISysDeptService deptService; + private final SysPermissionService permissionService; + + /** + * 获取角色信息列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/list") + public TableDataInfo list(SysRole role, PageQuery pageQuery) { + return roleService.selectPageRoleList(role, pageQuery); + } + + /** + * 导出角色信息列表 + */ + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:role:export") + @PostMapping("/export") + public void export(SysRole role, HttpServletResponse response) { + List list = roleService.selectRoleList(role); + ExcelUtil.exportExcel(list, "角色数据", SysRole.class, response); + } + + /** + * 根据角色编号获取详细信息 + * + * @param roleId 角色ID + */ + @SaCheckPermission("system:role:query") + @GetMapping(value = "/{roleId}") + public R getInfo(@PathVariable Long roleId) { + roleService.checkRoleDataScope(roleId); + return R.ok(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @SaCheckPermission("system:role:add") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysRole role) { + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) { + return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) { + return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) { + return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) { + return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + + if (roleService.updateRole(role) > 0) { + // 更新缓存用户权限 + LoginUser loginUser = getLoginUser(); + SysUser sysUser = userService.selectUserById(loginUser.getUserId()); + if (ObjectUtil.isNotNull(sysUser) && !sysUser.isAdmin()) { + loginUser.setMenuPermission(permissionService.getMenuPermission(sysUser)); + LoginHelper.setLoginUser(loginUser); + } + return R.ok(); + } + return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public R dataScope(@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + * + * @param roleIds 角色ID串 + */ + @SaCheckPermission("system:role:remove") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public R remove(@PathVariable Long[] roleIds) { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @SaCheckPermission("system:role:query") + @GetMapping("/optionselect") + public R> optionselect() { + return R.ok(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user, PageQuery pageQuery) { + return userService.selectAllocatedList(user, pageQuery); + } + + /** + * 查询未分配用户角色列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user, PageQuery pageQuery) { + return userService.selectUnallocatedList(user, pageQuery); + } + + /** + * 取消授权用户 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public R cancelAuthUser(@RequestBody SysUserRole userRole) { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + * + * @param roleId 角色ID + * @param userIds 用户ID串 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public R cancelAuthUserAll(Long roleId, Long[] userIds) { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + * + * @param roleId 角色ID + * @param userIds 用户ID串 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public R selectAuthUserAll(Long roleId, Long[] userIds) { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + * + * @param roleId 角色ID + */ + @SaCheckPermission("system:role:list") + @GetMapping(value = "/deptTree/{roleId}") + public R> roleDeptTreeselect(@PathVariable("roleId") Long roleId) { + Map ajax = new HashMap<>(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return R.ok(ajax); + } +} diff --git a/service/src/main/java/com/rzdata/web/controller/system/SysUserController.java b/service/src/main/java/com/rzdata/web/controller/system/SysUserController.java new file mode 100644 index 0000000..211ee0c --- /dev/null +++ b/service/src/main/java/com/rzdata/web/controller/system/SysUserController.java @@ -0,0 +1,253 @@ +package com.rzdata.web.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.constant.UserConstants; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.domain.entity.SysDept; +import com.rzdata.common.core.domain.entity.SysRole; +import com.rzdata.common.core.domain.entity.SysUser; +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.excel.ExcelResult; +import com.rzdata.common.helper.LoginHelper; +import com.rzdata.common.utils.StreamUtils; +import com.rzdata.common.utils.StringUtils; +import com.rzdata.common.utils.poi.ExcelUtil; +import com.rzdata.system.domain.vo.SysUserExportVo; +import com.rzdata.system.domain.vo.SysUserImportVo; +import com.rzdata.system.listener.SysUserImportListener; +import com.rzdata.system.service.ISysDeptService; +import com.rzdata.system.service.ISysPostService; +import com.rzdata.system.service.ISysRoleService; +import com.rzdata.system.service.ISysUserService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 用户信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController { + + private final ISysUserService userService; + private final ISysRoleService roleService; + private final ISysPostService postService; + private final ISysDeptService deptService; + + /** + * 获取用户列表 + */ + @SaCheckPermission("system:user:list") + @GetMapping("/list") + public TableDataInfo list(SysUser user, PageQuery pageQuery) { + return userService.selectPageUserList(user, pageQuery); + } + + /** + * 导出用户列表 + */ + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:user:export") + @PostMapping("/export") + public void export(SysUser user, HttpServletResponse response) { + List list = userService.selectUserList(user); + List listVo = BeanUtil.copyToList(list, SysUserExportVo.class); + for (int i = 0; i < list.size(); i++) { + SysDept dept = list.get(i).getDept(); + SysUserExportVo vo = listVo.get(i); + if (ObjectUtil.isNotEmpty(dept)) { + vo.setDeptName(dept.getDeptName()); + vo.setLeader(dept.getLeader()); + } + } + ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response); + } + + /** + * 导入数据 + * + * @param file 导入文件 + * @param updateSupport 是否更新已存在数据 + */ + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @SaCheckPermission("system:user:import") + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception { + ExcelResult result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport)); + return R.ok(result.getAnalysis()); + } + + /** + * 获取导入模板 + */ + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response); + } + + /** + * 根据用户编号获取详细信息 + * + * @param userId 用户ID + */ + @SaCheckPermission("system:user:query") + @GetMapping(value = {"/", "/{userId}"}) + public R> getInfo(@PathVariable(value = "userId", required = false) Long userId) { + userService.checkUserDataScope(userId); + Map ajax = new HashMap<>(); + List roles = roleService.selectRoleAll(); + ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin())); + ajax.put("posts", postService.selectPostAll()); + if (ObjectUtil.isNotNull(userId)) { + SysUser sysUser = userService.selectUserById(userId); + ajax.put("user", sysUser); + ajax.put("postIds", postService.selectPostListByUserId(userId)); + ajax.put("roleIds", StreamUtils.toList(sysUser.getRoles(), SysRole::getRoleId)); + } + return R.ok(ajax); + } + + /** + * 新增用户 + */ + @SaCheckPermission("system:user:add") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysUser user) { + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) { + return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setPassword(BCrypt.hashpw(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) { + return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + * + * @param userIds 角色ID串 + */ + @SaCheckPermission("system:user:remove") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public R remove(@PathVariable Long[] userIds) { + if (ArrayUtil.contains(userIds, getUserId())) { + return R.fail("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @SaCheckPermission("system:user:resetPwd") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public R resetPwd(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(BCrypt.hashpw(user.getPassword())); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + * + * @param userId 用户ID + */ + @SaCheckPermission("system:user:query") + @GetMapping("/authRole/{userId}") + public R> authRole(@PathVariable Long userId) { + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + Map ajax = new HashMap<>(); + ajax.put("user", user); + ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin())); + return R.ok(ajax); + } + + /** + * 用户授权角色 + * + * @param userId 用户Id + * @param roleIds 角色ID串 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public R insertAuthRole(Long userId, Long[] roleIds) { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return R.ok(); + } + + /** + * 获取部门树列表 + */ + @SaCheckPermission("system:user:list") + @GetMapping("/deptTree") + public R>> deptTree(SysDept dept) { + return R.ok(deptService.selectDeptTreeList(dept)); + } + +} diff --git a/service/src/main/resources/application-dev.yml b/service/src/main/resources/application-dev.yml new file mode 100644 index 0000000..aaaeca4 --- /dev/null +++ b/service/src/main/resources/application-dev.yml @@ -0,0 +1,169 @@ +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: rzdata + password: 123456 + +--- # xxl-job 配置 +xxl.job: + # 执行器开关 + enabled: true + # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。 + admin-addresses: http://192.168.1.169:9100/xxl-job-admin + # 执行器通讯TOKEN:非空时启用 + access-token: xxl-job + executor: + # 执行器AppName:执行器心跳注册分组依据;为空则关闭自动注册 + appname: xxl-job-executor + # 执行器端口号 执行器从9101开始往后写 + port: 9101 + # 执行器注册:默认IP:PORT + address: + # 执行器IP:默认自动获取IP + ip: + # 执行器运行日志文件存储磁盘路径 + logpath: ./logs/xxl-job + # 执行器日志文件保存天数:大于3生效 + logretentiondays: 30 + +--- # 数据源配置 +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + # 性能分析插件(有性能损耗 不建议生产环境使用) + p6spy: true + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 + # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) + url: jdbc:mysql://192.168.1.13:3307/nacos?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + username: fast + password: Fast@0922 + # 从库数据源 + slave: + lazy: true + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.1.13:3307/nacos?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + username: fast + password: Fast@0922 +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# hikari: +# connectionTestQuery: SELECT 1 FROM DUAL +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root + hikari: + # 最大连接池数量 + maxPoolSize: 20 + # 最小空闲线程数量 + minIdle: 10 + # 配置获取连接等待超时的时间 + connectionTimeout: 10000 + # 校验超时时间 + validationTimeout: 5000 + # 空闲连接存活最大时间,默认10分钟 + idleTimeout: 60000 + # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 + maxLifetime: 900000 + # 连接测试query(配置检测连接是否有效) + connectionTestQuery: SELECT 1 + +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) +spring: + redis: + # 地址 + host: 192.168.1.169 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码(如没有密码请注释掉) + # password: + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl: false + +redisson: + # redis key前缀 + keyPrefix: + # 线程池数量 + threads: 4 + # Netty线程池数量 + nettyThreads: 8 + # 单节点配置 + singleServerConfig: + # 客户端名称 + clientName: ${rzdata.name} + # 最小空闲连接数 + connectionMinimumIdleSize: 8 + # 连接池大小 + connectionPoolSize: 32 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 +sms: + enabled: false + # 阿里云 dysmsapi.aliyuncs.com + # 腾讯云 sms.tencentcloudapi.com + endpoint: "dysmsapi.aliyuncs.com" + accessKeyId: xxxxxxx + accessKeySecret: xxxxxx + signName: 测试 + # 腾讯专用 + sdkAppId: diff --git a/service/src/main/resources/application-prod.yml b/service/src/main/resources/application-prod.yml new file mode 100644 index 0000000..95da15b --- /dev/null +++ b/service/src/main/resources/application-prod.yml @@ -0,0 +1,172 @@ +--- # 临时文件存储位置 避免临时文件被系统清理报错 +spring.servlet.multipart.location: /rzdata/server/temp + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + username: rzdata + password: 123456 + +--- # xxl-job 配置 +xxl.job: + # 执行器开关 + enabled: true + # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。 + admin-addresses: http://localhost:9100/xxl-job-admin + # 执行器通讯TOKEN:非空时启用 + access-token: xxl-job + executor: + # 执行器AppName:执行器心跳注册分组依据;为空则关闭自动注册 + appname: xxl-job-executor + # 执行器端口号 执行器从9101开始往后写 + port: 9101 + # 执行器注册:默认IP:PORT + address: + # 执行器IP:默认自动获取IP + ip: + # 执行器运行日志文件存储磁盘路径 + logpath: ./logs/xxl-job + # 执行器日志文件保存天数:大于3生效 + logretentiondays: 30 + +--- # 数据源配置 +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + # 性能分析插件(有性能损耗 不建议生产环境使用) + p6spy: false + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 + # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + username: root + password: root + # 从库数据源 + slave: + lazy: true + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + username: + password: +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# hikari: +# connectionTestQuery: SELECT 1 FROM DUAL +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root + hikari: + # 最大连接池数量 + maxPoolSize: 20 + # 最小空闲线程数量 + minIdle: 10 + # 配置获取连接等待超时的时间 + connectionTimeout: 10000 + # 校验超时时间 + validationTimeout: 5000 + # 空闲连接存活最大时间,默认10分钟 + idleTimeout: 60000 + # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 + maxLifetime: 900000 + # 连接测试query(配置检测连接是否有效) + connectionTestQuery: SELECT 1 + +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) +spring: + redis: + # 地址 + host: localhost + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码(如没有密码请注释掉) + # password: + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl: false + +redisson: + # redis key前缀 + keyPrefix: + # 线程池数量 + threads: 16 + # Netty线程池数量 + nettyThreads: 32 + # 单节点配置 + singleServerConfig: + # 客户端名称 + clientName: ${rzdata.name} + # 最小空闲连接数 + connectionMinimumIdleSize: 32 + # 连接池大小 + connectionPoolSize: 64 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 +sms: + enabled: false + # 阿里云 dysmsapi.aliyuncs.com + # 腾讯云 sms.tencentcloudapi.com + endpoint: "dysmsapi.aliyuncs.com" + accessKeyId: xxxxxxx + accessKeySecret: xxxxxx + signName: 测试 + # 腾讯专用 + sdkAppId: diff --git a/service/src/main/resources/application.yml b/service/src/main/resources/application.yml new file mode 100644 index 0000000..955df88 --- /dev/null +++ b/service/src/main/resources/application.yml @@ -0,0 +1,255 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi-Vue-Plus + # 版本 + version: ${ruoyi-vue-plus.version} + # 版权年份 + copyrightYear: 2022 + # 实例演示开关 + demoEnabled: true + # 获取ip地址开关 + addressEnabled: true + # 缓存懒加载 + cacheLazy: false + +captcha: + # 页面 <参数设置> 可开启关闭 验证码校验 + # 验证码类型 math 数组计算 char 字符验证 + type: MATH + # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 + category: CIRCLE + # 数字验证码位数 + numberLength: 1 + # 字符验证码长度 + charLength: 4 + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8080 + servlet: + # 应用的访问路径 + context-path: / + # undertow 配置 + undertow: + # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的 + max-http-post-size: -1 + # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 + # 每块buffer的空间大小,越小的空间被利用越充分 + buffer-size: 512 + # 是否分配的直接内存 + direct-buffers: true + threads: + # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 + io: 8 + # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 + worker: 256 + +# 日志配置 +logging: + level: + com.rzdata: @logging.level@ + org.springframework: warn + config: classpath:logback.xml + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + application: + name: ${ruoyi.name} + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: @profiles.active@ + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + mvc: + format: + date-time: yyyy-MM-dd HH:mm:ss + jackson: + # 日期格式化 + date-format: yyyy-MM-dd HH:mm:ss + serialization: + # 格式化输出 + indent_output: false + # 忽略无法转换的对象 + fail_on_empty_beans: false + deserialization: + # 允许对象忽略json中不存在的属性 + fail_on_unknown_properties: false + +# Sa-Token配置 +sa-token: + # token名称 (同时也是cookie名称) + token-name: Authorization + # token有效期 设为一天 (必定过期) 单位: 秒 + timeout: 86400 + # token临时有效期 (指定时间无操作就过期) 单位: 秒 + activity-timeout: 1800 + # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + is-share: false + # 是否尝试从header里读取token + is-read-header: true + # 是否尝试从cookie里读取token + is-read-cookie: false + # token前缀 + token-prefix: "Bearer" + # jwt秘钥 + jwt-secret-key: abcdefghijklmnopqrstuvwxyz + +# security配置 +security: + # 排除路径 + excludes: + # 静态资源 + - /*.html + - /**/*.html + - /**/*.css + - /**/*.js + # swagger 文档配置 + - /favicon.ico + - /*/api-docs + - /*/api-docs/** + # actuator 监控配置 + - /actuator + - /actuator/** + +# MyBatisPlus配置 +# https://baomidou.com/config/ +mybatis-plus: + # 不支持多包, 如有需要可在注解配置 或 提升扫包等级 + # 例如 com.**.**.mapper + mapperPackage: com.rzdata.**.mapper + # 对应的 XML 文件位置 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: com.rzdata.**.domain + # 启动时是否检查 MyBatis XML 文件的存在,默认不检查 + checkConfigLocation: false + configuration: + # 自动驼峰命名规则(camel case)映射 + mapUnderscoreToCamelCase: true + # MyBatis 自动映射策略 + # NONE:不启用 PARTIAL:只对非嵌套 resultMap 自动映射 FULL:对所有 resultMap 自动映射 + autoMappingBehavior: PARTIAL + # MyBatis 自动映射时未知列或未知属性处理策 + # NONE:不做处理 WARNING:打印相关警告 FAILING:抛出异常和详细信息 + autoMappingUnknownColumnBehavior: NONE + # 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl + # 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl + # 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl + logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl + global-config: + # 是否打印 Logo banner + banner: true + dbConfig: + # 主键类型 + # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID + idType: ASSIGN_ID + # 逻辑已删除值 + logicDeleteValue: 2 + # 逻辑未删除值 + logicNotDeleteValue: 0 + # 字段验证策略之 insert,在 insert 的时候的字段验证策略 + # IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL + insertStrategy: NOT_NULL + # 字段验证策略之 update,在 update 的时候的字段验证策略 + updateStrategy: NOT_NULL + # 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件 + where-strategy: NOT_NULL + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + info: + # 标题 + title: '标题:${ruoyi.name}后台管理系统_接口文档' + # 描述 + description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' + # 版本 + version: '版本号: ${ruoyi-vue-plus.version}' + # 作者信息 + contact: + name: Lion Li + email: crazylionli@163.com + url: https://gitee.com/JavaLionLi/RuoYi-Vue-Plus + components: + # 鉴权方式配置 + security-schemes: + apiKey: + type: APIKEY + in: HEADER + name: ${sa-token.token-name} + +springdoc: + swagger-ui: + # 持久化认证数据 + persistAuthorization: true + #这里定义了两个分组,可定义多个,也可以不定义 + group-configs: + - group: 1.演示模块 + packages-to-scan: com.rzdata.demo + - group: 2.系统模块 + packages-to-scan: com.rzdata.web + - group: 3.代码生成模块 + packages-to-scan: com.rzdata.generator + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + +# 全局线程池相关配置 +thread-pool: + # 是否开启线程池 + enabled: false + # 队列最大长度 + queueCapacity: 128 + # 线程池维护线程所允许的空闲时间 + keepAliveSeconds: 300 + +--- # 分布式锁 lock4j 全局配置 +lock4j: + # 获取分布式锁超时时间,默认为 3000 毫秒 + acquire-timeout: 3000 + # 分布式锁的超时时间,默认为 30 秒 + expire: 30000 + +--- # Actuator 监控端点的配置项 +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: ALWAYS + logfile: + external-file: ./logs/sys-console.log diff --git a/service/src/main/resources/banner.txt b/service/src/main/resources/banner.txt new file mode 100644 index 0000000..f592dbe --- /dev/null +++ b/service/src/main/resources/banner.txt @@ -0,0 +1,8 @@ +Application Version: ${ruoyi-vue-plus.version} +Spring Boot Version: ${spring-boot.version} +__________ _____.___.__ ____ ____ __________.__ +\______ \__ __ ____\__ | |__| \ \ / /_ __ ____ \______ \ | __ __ ______ + | _/ | \/ _ \/ | | | ______ \ Y / | \_/ __ \ ______ | ___/ | | | \/ ___/ + | | \ | ( <_> )____ | | /_____/ \ /| | /\ ___/ /_____/ | | | |_| | /\___ \ + |____|_ /____/ \____// ______|__| \___/ |____/ \___ > |____| |____/____//____ > + \/ \/ \/ \/ diff --git a/service/src/main/resources/excel/单列表.xlsx b/service/src/main/resources/excel/单列表.xlsx new file mode 100644 index 0000000..0f7347d Binary files /dev/null and b/service/src/main/resources/excel/单列表.xlsx differ diff --git a/service/src/main/resources/excel/多列表.xlsx b/service/src/main/resources/excel/多列表.xlsx new file mode 100644 index 0000000..c7d11dc Binary files /dev/null and b/service/src/main/resources/excel/多列表.xlsx differ diff --git a/service/src/main/resources/generator.yml b/service/src/main/resources/generator.yml new file mode 100644 index 0000000..dc56776 --- /dev/null +++ b/service/src/main/resources/generator.yml @@ -0,0 +1,10 @@ +# 代码生成 +gen: + # 作者 + author: rzdata + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.rzdata.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ diff --git a/service/src/main/resources/i18n/messages.properties b/service/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..4ff55f4 --- /dev/null +++ b/service/src/main/resources/i18n/messages.properties @@ -0,0 +1,45 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=对不起, 您的账号:{0} 不存在. +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号:{0} 已被删除 +user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 +length.not.valid=长度必须在{min}到{max}个字符之间 +user.username.not.blank=用户名不能为空 +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.username.length.valid=账户长度必须在{min}到{max}个字符之间 +user.password.not.blank=用户密码不能为空 +user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 +user.password.not.valid=* 5-50个字符 +user.email.not.valid=邮箱格式错误 +user.phonenumber.not.blank=用户手机号不能为空 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.register.save.error=保存用户 {0} 失败,注册账号已存在 +user.register.error=注册失败,请联系系统管理人员 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] +repeat.submit.message=不允许重复提交,请稍候再试 +rate.limiter.message=访问过于频繁,请稍候再试 +sms.code.not.blank=短信验证码不能为空 +sms.code.retry.limit.count=短信验证码输入错误{0}次 +sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 +xcx.code.not.blank=小程序code不能为空 diff --git a/service/src/main/resources/i18n/messages_en_US.properties b/service/src/main/resources/i18n/messages_en_US.properties new file mode 100644 index 0000000..c0faca9 --- /dev/null +++ b/service/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,45 @@ +#错误消息 +not.null=* Required fill in +user.jcaptcha.error=Captcha error +user.jcaptcha.expire=Captcha invalid +user.not.exists=Sorry, your account: {0} does not exist +user.password.not.match=User does not exist/Password error +user.password.retry.limit.count=Password input error {0} times +user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes +user.password.delete=Sorry, your account:{0} has been deleted +user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator +role.blocked=Role disabled,please contact administrators +user.logout.success=Exit successful +length.not.valid=The length must be between {min} and {max} characters +user.username.not.blank=Username cannot be blank +user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number +user.username.length.valid=Account length must be between {min} and {max} characters +user.password.not.blank=Password cannot be empty +user.password.length.valid=Password length must be between {min} and {max} characters +user.password.not.valid=* 5-50 characters +user.email.not.valid=Mailbox format error +user.phonenumber.not.blank=Phone number cannot be blank +user.mobile.phone.number.not.valid=Phone number format error +user.login.success=Login successful +user.register.success=Register successful +user.register.save.error=Failed to save user {0}, The registered account already exists +user.register.error=Register failed, please contact system administrator +user.notfound=Please login again +user.forcelogout=The administrator is forced to exit,please login again +user.unknown.error=Unknown error, please login again +##文件上传消息 +upload.exceed.maxSize=The uploaded file size exceeds the limit file size!
the maximum allowed file size is:{0}MB! +upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters +##权限 +no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}] +no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}] +no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}] +no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}] +no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}] +no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}] +repeat.submit.message=Repeat submit is not allowed, please try again later +rate.limiter.message=Visit too frequently, please try again later +sms.code.not.blank=Sms code cannot be blank +sms.code.retry.limit.count=Sms code input error {0} times +sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes +xcx.code.not.blank=Mini program code cannot be blank diff --git a/service/src/main/resources/i18n/messages_zh_CN.properties b/service/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 0000000..4ff55f4 --- /dev/null +++ b/service/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,45 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=对不起, 您的账号:{0} 不存在. +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号:{0} 已被删除 +user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 +length.not.valid=长度必须在{min}到{max}个字符之间 +user.username.not.blank=用户名不能为空 +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.username.length.valid=账户长度必须在{min}到{max}个字符之间 +user.password.not.blank=用户密码不能为空 +user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 +user.password.not.valid=* 5-50个字符 +user.email.not.valid=邮箱格式错误 +user.phonenumber.not.blank=用户手机号不能为空 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.register.save.error=保存用户 {0} 失败,注册账号已存在 +user.register.error=注册失败,请联系系统管理人员 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] +repeat.submit.message=不允许重复提交,请稍候再试 +rate.limiter.message=访问过于频繁,请稍候再试 +sms.code.not.blank=短信验证码不能为空 +sms.code.retry.limit.count=短信验证码输入错误{0}次 +sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 +xcx.code.not.blank=小程序code不能为空 diff --git a/service/src/main/resources/logback.xml b/service/src/main/resources/logback.xml new file mode 100644 index 0000000..40fa33b --- /dev/null +++ b/service/src/main/resources/logback.xml @@ -0,0 +1,129 @@ + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + ${log.path}/sys-console.log + + + ${log.path}/sys-console.%d{yyyy-MM-dd}.log + + 1 + + + ${log.pattern} + utf-8 + + + + INFO + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + 0 + + 512 + + + + + + + + 0 + + 512 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/demo/TestDemoMapper.xml b/service/src/main/resources/mapper/demo/TestDemoMapper.xml new file mode 100644 index 0000000..6278aff --- /dev/null +++ b/service/src/main/resources/mapper/demo/TestDemoMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/demo/TestTreeMapper.xml b/service/src/main/resources/mapper/demo/TestTreeMapper.xml new file mode 100644 index 0000000..efeb1c1 --- /dev/null +++ b/service/src/main/resources/mapper/demo/TestTreeMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/service/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..6bdd13d --- /dev/null +++ b/service/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/generator/GenTableMapper.xml b/service/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 0000000..edd7fa0 --- /dev/null +++ b/service/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/package-info.md b/service/src/main/resources/mapper/package-info.md new file mode 100644 index 0000000..c938b1e --- /dev/null +++ b/service/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/service/src/main/resources/mapper/system/SysConfigMapper.xml b/service/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..51a36ce --- /dev/null +++ b/service/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysDeptMapper.xml b/service/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..b6cf194 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysDictDataMapper.xml b/service/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..ba0d5e6 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysDictTypeMapper.xml b/service/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..b512c39 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysLogininforMapper.xml b/service/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..f246c65 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysMenuMapper.xml b/service/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..68a3d75 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysNoticeMapper.xml b/service/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..15bfa35 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysOperLogMapper.xml b/service/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..32dce78 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysOssConfigMapper.xml b/service/src/main/resources/mapper/system/SysOssConfigMapper.xml new file mode 100644 index 0000000..c83e701 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysOssConfigMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysOssMapper.xml b/service/src/main/resources/mapper/system/SysOssMapper.xml new file mode 100644 index 0000000..b5fd4af --- /dev/null +++ b/service/src/main/resources/mapper/system/SysOssMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysPostMapper.xml b/service/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..e317d2d --- /dev/null +++ b/service/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/service/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..1bd2311 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysRoleMapper.xml b/service/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..705102f --- /dev/null +++ b/service/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, + r.role_name, + r.role_key, + r.role_sort, + r.data_scope, + r.menu_check_strictly, + r.dept_check_strictly, + r.status, + r.del_flag, + r.create_time, + r.remark + from sys_role r + left join sys_user_role sur on sur.role_id = r.role_id + left join sys_user u on u.user_id = sur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/service/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..35ce541 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysUserMapper.xml b/service/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..7ff3c13 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, + u.dept_id, + u.user_name, + u.nick_name, + u.user_type, + u.email, + u.avatar, + u.phonenumber, + u.password, + u.sex, + u.status, + u.del_flag, + u.login_ip, + u.login_date, + u.create_by, + u.create_time, + u.remark, + d.dept_id, + d.parent_id, + d.ancestors, + d.dept_name, + d.order_num, + d.leader, + d.status as dept_status, + r.role_id, + r.role_name, + r.role_key, + r.role_sort, + r.data_scope, + r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role sur on u.user_id = sur.user_id + left join sys_role r on r.role_id = sur.role_id + + + + + + + + + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysUserPostMapper.xml b/service/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..ca124e0 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/service/src/main/resources/mapper/system/SysUserRoleMapper.xml b/service/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..f0cbf36 --- /dev/null +++ b/service/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/service/src/main/resources/spy.properties b/service/src/main/resources/spy.properties new file mode 100644 index 0000000..abbd893 --- /dev/null +++ b/service/src/main/resources/spy.properties @@ -0,0 +1,28 @@ +# p6spy 性能分析插件配置文件 +modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory +# 自定义日志打印 +logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger +#日志输出到控制台 +appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger +# 使用日志系统记录 sql +#appender=com.p6spy.engine.spy.appender.Slf4JLogger +# 设置 p6spy driver 代理 +#deregisterdrivers=true +# 取消JDBC URL前缀 +useprefix=true +# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. +excludecategories=info,debug,result,commit,resultset +# 日期格式 +dateformat=yyyy-MM-dd HH:mm:ss +# SQL语句打印时间格式 +databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss +# 实际驱动可多个 +#driverlist=org.h2.Driver +# 是否开启慢SQL记录 +outagedetection=true +# 慢SQL记录标准 2 秒 +outagedetectioninterval=2 +# 是否过滤 Log +filter=true +# 过滤 Log 时所排除的 sql 关键字,以逗号分隔 +exclude=SELECT 1 diff --git a/service/src/main/resources/vm/java/bo.java.vm b/service/src/main/resources/vm/java/bo.java.vm new file mode 100644 index 0000000..9caaf0f --- /dev/null +++ b/service/src/main/resources/vm/java/bo.java.vm @@ -0,0 +1,54 @@ +package ${packageName}.domain.bo; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import javax.validation.constraints.*; + +#foreach ($import in $importList) +import ${import}; +#end +#if($table.crud || $table.sub) +#elseif($table.tree) +#end + +/** + * ${functionName}业务对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity<${ClassName}Bo>") +#end + +@Data +@EqualsAndHashCode(callSuper = true) +public class ${ClassName}Bo extends ${Entity} { + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.isInsert || $column.isEdit)) + /** + * $column.columnComment + */ +#if($column.isInsert && $column.isEdit) +#set($Group="AddGroup.class, EditGroup.class") +#elseif($column.isInsert) +#set($Group="AddGroup.class") +#elseif($column.isEdit) +#set($Group="EditGroup.class") +#end +#if($column.isRequired == 1) +#if($column.javaType == 'String') + @NotBlank(message = "$column.columnComment不能为空", groups = { $Group }) +#else + @NotNull(message = "$column.columnComment不能为空", groups = { $Group }) +#end +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/service/src/main/resources/vm/java/controller.java.vm b/service/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..cfd7459 --- /dev/null +++ b/service/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,116 @@ +package ${packageName}.controller; + +import java.util.List; +import java.util.Arrays; + +import lombok.RequiredArgsConstructor; +import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.rzdata.common.annotation.RepeatSubmit; +import com.rzdata.common.annotation.Log; +import com.rzdata.common.core.controller.BaseController; +import com.rzdata.common.core.domain.PageQuery; +import com.rzdata.common.core.domain.R; +import com.rzdata.common.core.validate.AddGroup; +import com.rzdata.common.core.validate.EditGroup; +import com.rzdata.common.enums.BusinessType; +import com.rzdata.common.utils.poi.ExcelUtil; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.service.I${ClassName}Service; +#if($table.crud || $table.sub) +import com.rzdata.common.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName} + * + * @author ${author} + * @date ${datetime} + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController { + + private final I${ClassName}Service i${ClassName}Service; + + /** + * 查询${functionName}列表 + */ + @SaCheckPermission("${permissionPrefix}:list") + @GetMapping("/list") +#if($table.crud || $table.sub) + public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { + return i${ClassName}Service.queryPageList(bo, pageQuery); + } +#elseif($table.tree) + public R> list(${ClassName}Bo bo) { + List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo); + return R.ok(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @SaCheckPermission("${permissionPrefix}:export") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(${ClassName}Bo bo, HttpServletResponse response) { + List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo); + ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response); + } + + /** + * 获取${functionName}详细信息 + * + * @param ${pkColumn.javaField} 主键 + */ + @SaCheckPermission("${permissionPrefix}:query") + @GetMapping("/{${pkColumn.javaField}}") + public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空") + @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) { + return R.ok(i${ClassName}Service.queryById(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @SaCheckPermission("${permissionPrefix}:add") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) { + return toAjax(i${ClassName}Service.insertByBo(bo)); + } + + /** + * 修改${functionName} + */ + @SaCheckPermission("${permissionPrefix}:edit") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) { + return toAjax(i${ClassName}Service.updateByBo(bo)); + } + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField}s 主键串 + */ + @SaCheckPermission("${permissionPrefix}:remove") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { + return toAjax(i${ClassName}Service.deleteWithValidByIds(Arrays.asList(${pkColumn.javaField}s), true)); + } +} diff --git a/service/src/main/resources/vm/java/domain.java.vm b/service/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..eb075ab --- /dev/null +++ b/service/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,50 @@ +package ${packageName}.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +#foreach ($import in $importList) +import ${import}; +#end +#if($table.crud || $table.sub) +#elseif($table.tree) +#end + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) + #set($Entity="BaseEntity") +#elseif($table.tree) + #set($Entity="TreeEntity<${ClassName}>") +#end +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("${tableName}") +public class ${ClassName} extends ${Entity} { + + private static final long serialVersionUID=1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** + * $column.columnComment + */ +#if($column.javaField=='delFlag') + @TableLogic +#end +#if($column.javaField=='version') + @Version +#end +#if($column.isPk==1) + @TableId(value = "$column.columnName") +#end + private $column.javaType $column.javaField; +#end +#end + +} diff --git a/service/src/main/resources/vm/java/mapper.java.vm b/service/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..d22700a --- /dev/null +++ b/service/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,15 @@ +package ${packageName}.mapper; + +import ${packageName}.domain.${ClassName}; +import ${packageName}.domain.vo.${ClassName}Vo; +import com.rzdata.common.core.mapper.BaseMapperPlus; + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}Mapper, ${ClassName}, ${ClassName}Vo> { + +} diff --git a/service/src/main/resources/vm/java/service.java.vm b/service/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..47e572f --- /dev/null +++ b/service/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,53 @@ +package ${packageName}.service; + +import ${packageName}.domain.${ClassName}; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.bo.${ClassName}Bo; +#if($table.crud || $table.sub) +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.core.domain.PageQuery; +#end + +import java.util.Collection; +import java.util.List; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service { + + /** + * 查询${functionName} + */ + ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); + +#if($table.crud || $table.sub) + /** + * 查询${functionName}列表 + */ + TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery); +#end + + /** + * 查询${functionName}列表 + */ + List<${ClassName}Vo> queryList(${ClassName}Bo bo); + + /** + * 新增${functionName} + */ + Boolean insertByBo(${ClassName}Bo bo); + + /** + * 修改${functionName} + */ + Boolean updateByBo(${ClassName}Bo bo); + + /** + * 校验并批量删除${functionName}信息 + */ + Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid); +} diff --git a/service/src/main/resources/vm/java/serviceImpl.java.vm b/service/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..9d8bd2f --- /dev/null +++ b/service/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,133 @@ +package ${packageName}.service.impl; + +import cn.hutool.core.bean.BeanUtil; + #if($table.crud || $table.sub) +import com.rzdata.common.core.page.TableDataInfo; +import com.rzdata.common.core.domain.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +#end +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.${ClassName}; +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.service.I${ClassName}Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@RequiredArgsConstructor +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service { + + private final ${ClassName}Mapper baseMapper; + + /** + * 查询${functionName} + */ + @Override + public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){ + return baseMapper.selectVoById(${pkColumn.javaField}); + } + +#if($table.crud || $table.sub) + /** + * 查询${functionName}列表 + */ + @Override + public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) { + LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); + Page<${ClassName}Vo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } +#end + + /** + * 查询${functionName}列表 + */ + @Override + public List<${ClassName}Vo> queryList(${ClassName}Bo bo) { + LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery(); +#foreach($column in $columns) +#if($column.query) +#set($queryType=$column.queryType) +#set($javaField=$column.javaField) +#set($javaType=$column.javaType) +#set($columnName=$column.columnName) +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#set($mpMethod=$column.queryType.toLowerCase()) +#if($queryType != 'BETWEEN') +#if($javaType == 'String') +#set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())') +#else +#set($condition='bo.get'+$AttrName+'() != null') +#end + lqw.$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName()); +#else + lqw.between(params.get("begin$AttrName") != null && params.get("end$AttrName") != null, + ${ClassName}::get$AttrName ,params.get("begin$AttrName"), params.get("end$AttrName")); +#end +#end +#end + return lqw; + } + + /** + * 新增${functionName} + */ + @Override + public Boolean insertByBo(${ClassName}Bo bo) { + ${ClassName} add = BeanUtil.toBean(bo, ${ClassName}.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; +#set($pk=$pkColumn.javaField.substring(0,1).toUpperCase() + ${pkColumn.javaField.substring(1)}) + if (flag) { + bo.set$pk(add.get$pk()); + } + return flag; + } + + /** + * 修改${functionName} + */ + @Override + public Boolean updateByBo(${ClassName}Bo bo) { + ${ClassName} update = BeanUtil.toBean(bo, ${ClassName}.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(${ClassName} entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除${functionName} + */ + @Override + public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/service/src/main/resources/vm/java/sub-domain.java.vm b/service/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..fd3010d --- /dev/null +++ b/service/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,73 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import com.rzdata.common.annotation.Excel; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/service/src/main/resources/vm/java/vo.java.vm b/service/src/main/resources/vm/java/vo.java.vm new file mode 100644 index 0000000..ae5f3de --- /dev/null +++ b/service/src/main/resources/vm/java/vo.java.vm @@ -0,0 +1,50 @@ +package ${packageName}.domain.vo; + +#foreach ($import in $importList) +import ${import}; +#end +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.rzdata.common.annotation.ExcelDictFormat; +import com.rzdata.common.convert.ExcelDictConvert; +import lombok.Data; + + +/** + * ${functionName}视图对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@ExcelIgnoreUnannotated +public class ${ClassName}Vo { + + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if($column.isList) + /** + * $column.columnComment + */ +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if(${column.dictType} && ${column.dictType} != '') + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "${column.dictType}") +#elseif($parentheseIndex != -1) + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "$column.readConverterExp()") +#else + @ExcelProperty(value = "${comment}") +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/service/src/main/resources/vm/js/api.js.vm b/service/src/main/resources/vm/js/api.js.vm new file mode 100644 index 0000000..9295524 --- /dev/null +++ b/service/src/main/resources/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/service/src/main/resources/vm/sql/oracle/sql.vm b/service/src/main/resources/vm/sql/oracle/sql.vm new file mode 100644 index 0000000..3de2fa4 --- /dev/null +++ b/service/src/main/resources/vm/sql/oracle/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate, '', null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate, '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate, '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate, '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate, '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate, '', null, ''); diff --git a/service/src/main/resources/vm/sql/postgres/sql.vm b/service/src/main/resources/vm/sql/postgres/sql.vm new file mode 100644 index 0000000..e8b45c9 --- /dev/null +++ b/service/src/main/resources/vm/sql/postgres/sql.vm @@ -0,0 +1,20 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', now(), '', null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', now(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', now(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', now(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', now(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', now(), '', null, ''); + diff --git a/service/src/main/resources/vm/sql/sql.vm b/service/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..9bc0b02 --- /dev/null +++ b/service/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); diff --git a/service/src/main/resources/vm/sql/sqlserver/sql.vm b/service/src/main/resources/vm/sql/sqlserver/sql.vm new file mode 100644 index 0000000..956534f --- /dev/null +++ b/service/src/main/resources/vm/sql/sqlserver/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', getdate(), '', null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', getdate(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', getdate(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', getdate(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', getdate(), '', null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', getdate(), '', null, ''); diff --git a/service/src/main/resources/vm/vue/index-tree.vue.vm b/service/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..2bc699a --- /dev/null +++ b/service/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,517 @@ + + + diff --git a/service/src/main/resources/vm/vue/index.vue.vm b/service/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..e645b6b --- /dev/null +++ b/service/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,614 @@ + + + diff --git a/service/src/main/resources/vm/vue/v3/index-tree.vue.vm b/service/src/main/resources/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 0000000..663893a --- /dev/null +++ b/service/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,485 @@ + + + diff --git a/service/src/main/resources/vm/vue/v3/index.vue.vm b/service/src/main/resources/vm/vue/v3/index.vue.vm new file mode 100644 index 0000000..a93922e --- /dev/null +++ b/service/src/main/resources/vm/vue/v3/index.vue.vm @@ -0,0 +1,601 @@ + + + diff --git a/service/src/main/resources/vm/vue/v3/readme.txt b/service/src/main/resources/vm/vue/v3/readme.txt new file mode 100644 index 0000000..e6dc758 --- /dev/null +++ b/service/src/main/resources/vm/vue/v3/readme.txt @@ -0,0 +1 @@ +如果使用的是Vue3前端,那么需要覆盖一下此目录的模板index.vue.vm、index-tree.vue.vm文件到上级vue目录。 diff --git a/service/src/main/resources/vm/xml/mapper.xml.vm b/service/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..2a128fb --- /dev/null +++ b/service/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,14 @@ + + + + + +#foreach ($column in $columns) + +#end + + + + diff --git a/service/src/test/java/com/rzdata/test/AssertUnitTest.java b/service/src/test/java/com/rzdata/test/AssertUnitTest.java new file mode 100644 index 0000000..9f765bd --- /dev/null +++ b/service/src/test/java/com/rzdata/test/AssertUnitTest.java @@ -0,0 +1,45 @@ +package com.rzdata.test; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * 断言单元测试案例 + * + * @author Lion Li + */ +@DisplayName("断言单元测试案例") +public class AssertUnitTest { + + @DisplayName("测试 assertEquals 方法") + @Test + public void testAssertEquals() { + Assertions.assertEquals("666", new String("666")); + Assertions.assertNotEquals("666", new String("666")); + } + + @DisplayName("测试 assertSame 方法") + @Test + public void testAssertSame() { + Object obj = new Object(); + Object obj1 = obj; + Assertions.assertSame(obj, obj1); + Assertions.assertNotSame(obj, obj1); + } + + @DisplayName("测试 assertTrue 方法") + @Test + public void testAssertTrue() { + Assertions.assertTrue(true); + Assertions.assertFalse(true); + } + + @DisplayName("测试 assertNull 方法") + @Test + public void testAssertNull() { + Assertions.assertNull(null); + Assertions.assertNotNull(null); + } + +} diff --git a/service/src/test/java/com/rzdata/test/DemoUnitTest.java b/service/src/test/java/com/rzdata/test/DemoUnitTest.java new file mode 100644 index 0000000..c95273e --- /dev/null +++ b/service/src/test/java/com/rzdata/test/DemoUnitTest.java @@ -0,0 +1,70 @@ +package com.rzdata.test; + +import com.rzdata.common.config.RuoYiConfig; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.concurrent.TimeUnit; + +/** + * 单元测试案例 + * + * @author Lion Li + */ +@SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件 +@DisplayName("单元测试案例") +public class DemoUnitTest { + + @Autowired + private RuoYiConfig ruoYiConfig; + + @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解") + @Test + public void testTest() { + System.out.println(ruoYiConfig); + } + + @Disabled + @DisplayName("测试 @Disabled 注解") + @Test + public void testDisabled() { + System.out.println(ruoYiConfig); + } + + @Timeout(value = 2L, unit = TimeUnit.SECONDS) + @DisplayName("测试 @Timeout 注解") + @Test + public void testTimeout() throws InterruptedException { + Thread.sleep(3000); + System.out.println(ruoYiConfig); + } + + + @DisplayName("测试 @RepeatedTest 注解") + @RepeatedTest(3) + public void testRepeatedTest() { + System.out.println(666); + } + + @BeforeAll + public static void testBeforeAll() { + System.out.println("@BeforeAll =================="); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + @AfterAll + public static void testAfterAll() { + System.out.println("@AfterAll =================="); + } + +} diff --git a/service/src/test/java/com/rzdata/test/ParamUnitTest.java b/service/src/test/java/com/rzdata/test/ParamUnitTest.java new file mode 100644 index 0000000..05a398e --- /dev/null +++ b/service/src/test/java/com/rzdata/test/ParamUnitTest.java @@ -0,0 +1,72 @@ +package com.rzdata.test; + +import com.rzdata.common.enums.UserType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +/** + * 带参数单元测试案例 + * + * @author Lion Li + */ +@DisplayName("带参数单元测试案例") +public class ParamUnitTest { + + @DisplayName("测试 @ValueSource 注解") + @ParameterizedTest + @ValueSource(strings = {"t1", "t2", "t3"}) + public void testValueSource(String str) { + System.out.println(str); + } + + @DisplayName("测试 @NullSource 注解") + @ParameterizedTest + @NullSource + public void testNullSource(String str) { + System.out.println(str); + } + + @DisplayName("测试 @EnumSource 注解") + @ParameterizedTest + @EnumSource(UserType.class) + public void testEnumSource(UserType type) { + System.out.println(type.getUserType()); + } + + @DisplayName("测试 @MethodSource 注解") + @ParameterizedTest + @MethodSource("getParam") + public void testMethodSource(String str) { + System.out.println(str); + } + + public static Stream getParam() { + List list = new ArrayList<>(); + list.add("t1"); + list.add("t2"); + list.add("t3"); + return list.stream(); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + +} diff --git a/service/src/test/java/com/rzdata/test/TagUnitTest.java b/service/src/test/java/com/rzdata/test/TagUnitTest.java new file mode 100644 index 0000000..0263436 --- /dev/null +++ b/service/src/test/java/com/rzdata/test/TagUnitTest.java @@ -0,0 +1,54 @@ +package com.rzdata.test; + +import org.junit.jupiter.api.*; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * 标签单元测试案例 + * + * @author Lion Li + */ +@SpringBootTest +@DisplayName("标签单元测试案例") +public class TagUnitTest { + + @Tag("dev") + @DisplayName("测试 @Tag dev") + @Test + public void testTagDev() { + System.out.println("dev"); + } + + @Tag("prod") + @DisplayName("测试 @Tag prod") + @Test + public void testTagProd() { + System.out.println("prod"); + } + + @Tag("local") + @DisplayName("测试 @Tag local") + @Test + public void testTagLocal() { + System.out.println("local"); + } + + @Tag("exclude") + @DisplayName("测试 @Tag exclude") + @Test + public void testTagExclude() { + System.out.println("exclude"); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + +} diff --git a/ui/.editorconfig b/ui/.editorconfig new file mode 100644 index 0000000..7034f9b --- /dev/null +++ b/ui/.editorconfig @@ -0,0 +1,22 @@ +# 告诉EditorConfig插件,这是根文件,不用继续往上查找 +root = true + +# 匹配全部文件 +[*] +# 设置字符集 +charset = utf-8 +# 缩进风格,可选space、tab +indent_style = space +# 缩进的空格数 +indent_size = 2 +# 结尾换行符,可选lf、cr、crlf +end_of_line = lf +# 在文件结尾插入新行 +insert_final_newline = true +# 删除一行中的前后空格 +trim_trailing_whitespace = true + +# 匹配md结尾的文件 +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/ui/.env.development b/ui/.env.development new file mode 100644 index 0000000..232d907 --- /dev/null +++ b/ui/.env.development @@ -0,0 +1,20 @@ +# 页面标题 +VUE_APP_TITLE = RuoYi-Vue-Plus后台管理系统 + +# 开发环境配置 +ENV = 'development' + +# 若依管理系统/开发环境 +VUE_APP_BASE_API = '/dev-api' + +# 应用访问路径 例如使用前缀 /admin/ +VUE_APP_CONTEXT_PATH = '/' + +# 监控地址 +VUE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/login' + +# xxl-job 控制台地址 +VUE_APP_XXL_JOB_ADMIN = 'http://localhost:9100/xxl-job-admin' + +# 路由懒加载 +VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/ui/.env.production b/ui/.env.production new file mode 100644 index 0000000..928000b --- /dev/null +++ b/ui/.env.production @@ -0,0 +1,17 @@ +# 页面标题 +VUE_APP_TITLE = RuoYi-Vue-Plus后台管理系统 + +# 生产环境配置 +ENV = 'production' + +# 应用访问路径 例如使用前缀 /admin/ +VUE_APP_CONTEXT_PATH = '/' + +# 监控地址 +VUE_APP_MONITRO_ADMIN = '/admin/login' + +# 监控地址 +VUE_APP_XXL_JOB_ADMIN = '/xxl-job-admin' + +# 若依管理系统/生产环境 +VUE_APP_BASE_API = '/prod-api' diff --git a/ui/.eslintignore b/ui/.eslintignore new file mode 100644 index 0000000..89be6f6 --- /dev/null +++ b/ui/.eslintignore @@ -0,0 +1,10 @@ +# 忽略build目录下类型为js的文件的语法检查 +build/*.js +# 忽略src/assets目录下文件的语法检查 +src/assets +# 忽略public目录下文件的语法检查 +public +# 忽略当前目录下为js的文件的语法检查 +*.js +# 忽略当前目录下为vue的文件的语法检查 +*.vue \ No newline at end of file diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js new file mode 100644 index 0000000..82bbdee --- /dev/null +++ b/ui/.eslintrc.js @@ -0,0 +1,199 @@ +// ESlint 检查配置 +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint', + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: ['plugin:vue/recommended', 'eslint:recommended'], + + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + rules: { + "vue/max-attributes-per-line": [2, { + "singleline": 10, + "multiline": { + "max": 1, + "allowFirstLine": false + } + }], + "vue/singleline-html-element-content-newline": "off", + "vue/multiline-html-element-content-newline":"off", + "vue/name-property-casing": ["error", "PascalCase"], + "vue/no-v-html": "off", + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': ["error", "always", {"null": "ignore"}], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 0, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/ui/.gitignore b/ui/.gitignore new file mode 100644 index 0000000..78a752d --- /dev/null +++ b/ui/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/ui/README.md b/ui/README.md new file mode 100644 index 0000000..7149ce0 --- /dev/null +++ b/ui/README.md @@ -0,0 +1,30 @@ +## 开发 + +```bash +# 克隆项目 +git clone https://gitee.com/y_project/RuoYi-Vue + +# 进入项目目录 +cd rzdata-ui + +# 安装依赖 +npm install + +# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 +npm install --registry=https://registry.npmmirror.com + +# 启动服务 +npm run dev +``` + +浏览器访问 http://localhost:80 + +## 发布 + +```bash +# 构建测试环境 +npm run build:stage + +# 构建生产环境 +npm run build:prod +``` \ No newline at end of file diff --git a/ui/babel.config.js b/ui/babel.config.js new file mode 100644 index 0000000..b99f001 --- /dev/null +++ b/ui/babel.config.js @@ -0,0 +1,13 @@ +module.exports = { + presets: [ + // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app + '@vue/cli-plugin-babel/preset' + ], + 'env': { + 'development': { + // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). + // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. + 'plugins': ['dynamic-import-node'] + } + } +} diff --git a/ui/bin/build.bat b/ui/bin/build.bat new file mode 100644 index 0000000..dda590d --- /dev/null +++ b/ui/bin/build.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] Weḅdistļ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run build:prod + +pause \ No newline at end of file diff --git a/ui/bin/package.bat b/ui/bin/package.bat new file mode 100644 index 0000000..2f22a4a --- /dev/null +++ b/ui/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [信息] 安装Web工程,生成node_modules文件。 +echo. + +%~d0 +cd %~dp0 + +cd .. +npm install --registry=https://registry.npmmirror.com + +pause diff --git a/ui/bin/run-web.bat b/ui/bin/run-web.bat new file mode 100644 index 0000000..0318c52 --- /dev/null +++ b/ui/bin/run-web.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [信息] 使用 Vue CLI 命令运行 Web 工程。 +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run dev + +pause \ No newline at end of file diff --git a/ui/build/index.js b/ui/build/index.js new file mode 100644 index 0000000..0c57de2 --- /dev/null +++ b/ui/build/index.js @@ -0,0 +1,35 @@ +const { run } = require('runjs') +const chalk = require('chalk') +const config = require('../vue.config.js') +const rawArgv = process.argv.slice(2) +const args = rawArgv.join(' ') + +if (process.env.npm_config_preview || rawArgv.includes('--preview')) { + const report = rawArgv.includes('--report') + + run(`vue-cli-service build ${args}`) + + const port = 9526 + const publicPath = config.publicPath + + var connect = require('connect') + var serveStatic = require('serve-static') + const app = connect() + + app.use( + publicPath, + serveStatic('./dist', { + index: ['index.html', '/'] + }) + ) + + app.listen(port, function () { + console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) + if (report) { + console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) + } + + }) +} else { + run(`vue-cli-service build ${args}`) +} diff --git a/ui/package.json b/ui/package.json new file mode 100644 index 0000000..c197583 --- /dev/null +++ b/ui/package.json @@ -0,0 +1,89 @@ +{ + "name": "ruoyi-vue-plus", + "version": "4.5.0", + "description": "RuoYi-Vue-Plus后台管理系统", + "author": "LionLi", + "license": "MIT", + "scripts": { + "dev": "vue-cli-service serve", + "build:prod": "vue-cli-service build", + "preview": "node build/index.js --preview", + "lint": "eslint --ext .js,.vue src" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "src/**/*.{js,vue}": [ + "eslint --fix", + "git add" + ] + }, + "keywords": [ + "vue", + "admin", + "dashboard", + "element-ui", + "boilerplate", + "admin-template", + "management-system" + ], + "repository": { + "type": "git", + "url": "https://gitee.com/y_project/RuoYi-Vue.git" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "axios": "0.24.0", + "clipboard": "2.0.8", + "core-js": "3.25.3", + "echarts": "5.4.0", + "element-ui": "2.15.10", + "file-saver": "2.0.5", + "fuse.js": "6.4.3", + "highlight.js": "9.18.5", + "js-beautify": "1.13.0", + "js-cookie": "3.0.1", + "jsencrypt": "3.0.0-rc.1", + "nprogress": "0.2.0", + "quill": "1.3.7", + "screenfull": "5.0.2", + "sortablejs": "1.10.2", + "vue": "2.6.12", + "vue-count-to": "1.0.13", + "vue-cropper": "0.5.5", + "vue-meta": "2.4.0", + "vue-router": "3.4.9", + "vuedraggable": "2.24.3", + "vuex": "3.6.0" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "4.4.6", + "@vue/cli-plugin-eslint": "4.4.6", + "@vue/cli-service": "4.4.6", + "babel-eslint": "10.1.0", + "babel-plugin-dynamic-import-node": "2.3.3", + "chalk": "4.1.0", + "compression-webpack-plugin": "5.0.2", + "connect": "3.6.6", + "eslint": "7.15.0", + "eslint-plugin-vue": "7.2.0", + "lint-staged": "10.5.3", + "runjs": "4.4.2", + "sass": "1.32.13", + "sass-loader": "10.1.1", + "script-ext-html-webpack-plugin": "2.1.5", + "svg-sprite-loader": "5.1.1", + "vue-template-compiler": "2.6.12" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ] +} diff --git a/ui/public/favicon.ico b/ui/public/favicon.ico new file mode 100644 index 0000000..e263760 Binary files /dev/null and b/ui/public/favicon.ico differ diff --git a/ui/public/html/ie.html b/ui/public/html/ie.html new file mode 100644 index 0000000..052ffcd --- /dev/null +++ b/ui/public/html/ie.html @@ -0,0 +1,46 @@ + + + + + + 请升级您的浏览器 + + + + + + +

请升级您的浏览器,以便我们更好的为您提供服务!

+

您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。

+
+

请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束

+

自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明

+
+

您可以选择更先进的浏览器

+

推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。

+ +
+ + \ No newline at end of file diff --git a/ui/public/index.html b/ui/public/index.html new file mode 100644 index 0000000..e32e129 --- /dev/null +++ b/ui/public/index.html @@ -0,0 +1,208 @@ + + + + + + + + + <%= webpackConfig.name %> + + + + +
+
+
+
+
+
正在加载系统资源,请耐心等待
+
+
+ + diff --git a/ui/public/robots.txt b/ui/public/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/ui/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/ui/src/App.vue b/ui/src/App.vue new file mode 100644 index 0000000..29de49f --- /dev/null +++ b/ui/src/App.vue @@ -0,0 +1,28 @@ + + + + diff --git a/ui/src/api/demo/demo.js b/ui/src/api/demo/demo.js new file mode 100644 index 0000000..04d4025 --- /dev/null +++ b/ui/src/api/demo/demo.js @@ -0,0 +1,54 @@ +import request from '@/utils/request' + +// 查询测试单表列表 +export function listDemo(query) { + return request({ + url: '/demo/demo/list', + method: 'get', + params: query + }) +} + +// 自定义分页接口 +export function pageDemo(query) { + return request({ + url: '/demo/demo/page', + method: 'get', + params: query + }) +} + +// 查询测试单表详细 +export function getDemo(id) { + return request({ + url: '/demo/demo/' + id, + method: 'get' + }) +} + +// 新增测试单表 +export function addDemo(data) { + return request({ + url: '/demo/demo', + method: 'post', + data: data + }) +} + +// 修改测试单表 +export function updateDemo(data) { + return request({ + url: '/demo/demo', + method: 'put', + data: data + }) +} + +// 删除测试单表 +export function delDemo(id) { + return request({ + url: '/demo/demo/' + id, + method: 'delete' + }) +} + diff --git a/ui/src/api/demo/tree.js b/ui/src/api/demo/tree.js new file mode 100644 index 0000000..4c7ebc0 --- /dev/null +++ b/ui/src/api/demo/tree.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询测试树表列表 +export function listTree(query) { + return request({ + url: '/demo/tree/list', + method: 'get', + params: query + }) +} + +// 查询测试树表详细 +export function getTree(id) { + return request({ + url: '/demo/tree/' + id, + method: 'get' + }) +} + +// 新增测试树表 +export function addTree(data) { + return request({ + url: '/demo/tree', + method: 'post', + data: data + }) +} + +// 修改测试树表 +export function updateTree(data) { + return request({ + url: '/demo/tree', + method: 'put', + data: data + }) +} + +// 删除测试树表 +export function delTree(id) { + return request({ + url: '/demo/tree/' + id, + method: 'delete' + }) +} diff --git a/ui/src/api/login.js b/ui/src/api/login.js new file mode 100644 index 0000000..3c5bcfe --- /dev/null +++ b/ui/src/api/login.js @@ -0,0 +1,71 @@ +import request from '@/utils/request' + +// 登录方法 +export function login(username, password, code, uuid) { + const data = { + username, + password, + code, + uuid + } + return request({ + url: '/login', + headers: { + isToken: false + }, + method: 'post', + data: data + }) +} + +// 注册方法 +export function register(data) { + return request({ + url: '/register', + headers: { + isToken: false + }, + method: 'post', + data: data + }) +} + +// 获取用户详细信息 +export function getInfo() { + return request({ + url: '/getInfo', + method: 'get' + }) +} + +// 退出方法 +export function logout() { + return request({ + url: '/logout', + method: 'post' + }) +} + +// 获取验证码 +export function getCodeImg() { + return request({ + url: '/captchaImage', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }) +} + +// 短信验证码 +export function getCodeSms() { + return request({ + url: '/captchaSms', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }) +} diff --git a/ui/src/api/menu.js b/ui/src/api/menu.js new file mode 100644 index 0000000..faef101 --- /dev/null +++ b/ui/src/api/menu.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取路由 +export const getRouters = () => { + return request({ + url: '/getRouters', + method: 'get' + }) +} \ No newline at end of file diff --git a/ui/src/api/monitor/cache.js b/ui/src/api/monitor/cache.js new file mode 100644 index 0000000..5cf0517 --- /dev/null +++ b/ui/src/api/monitor/cache.js @@ -0,0 +1,57 @@ +import request from '@/utils/request' + +// 查询缓存详细 +export function getCache() { + return request({ + url: '/monitor/cache', + method: 'get' + }) +} + +// 查询缓存名称列表 +export function listCacheName() { + return request({ + url: '/monitor/cache/getNames', + method: 'get' + }) +} + +// 查询缓存键名列表 +export function listCacheKey(cacheName) { + return request({ + url: '/monitor/cache/getKeys/' + cacheName, + method: 'get' + }) +} + +// 查询缓存内容 +export function getCacheValue(cacheName, cacheKey) { + return request({ + url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, + method: 'get' + }) +} + +// 清理指定名称缓存 +export function clearCacheName(cacheName) { + return request({ + url: '/monitor/cache/clearCacheName/' + cacheName, + method: 'delete' + }) +} + +// 清理指定键名缓存 +export function clearCacheKey(cacheName, cacheKey) { + return request({ + url: '/monitor/cache/clearCacheKey/'+ cacheName + "/" + cacheKey, + method: 'delete' + }) +} + +// 清理全部缓存 +export function clearCacheAll() { + return request({ + url: '/monitor/cache/clearCacheAll', + method: 'delete' + }) +} diff --git a/ui/src/api/monitor/logininfor.js b/ui/src/api/monitor/logininfor.js new file mode 100644 index 0000000..4d112b7 --- /dev/null +++ b/ui/src/api/monitor/logininfor.js @@ -0,0 +1,34 @@ +import request from '@/utils/request' + +// 查询登录日志列表 +export function list(query) { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }) +} + +// 删除登录日志 +export function delLogininfor(infoId) { + return request({ + url: '/monitor/logininfor/' + infoId, + method: 'delete' + }) +} + +// 解锁用户登录状态 +export function unlockLogininfor(userName) { + return request({ + url: '/monitor/logininfor/unlock/' + userName, + method: 'get' + }) +} + +// 清空登录日志 +export function cleanLogininfor() { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete' + }) +} diff --git a/ui/src/api/monitor/online.js b/ui/src/api/monitor/online.js new file mode 100644 index 0000000..bd22137 --- /dev/null +++ b/ui/src/api/monitor/online.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 查询在线用户列表 +export function list(query) { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }) +} + +// 强退用户 +export function forceLogout(tokenId) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }) +} diff --git a/ui/src/api/monitor/operlog.js b/ui/src/api/monitor/operlog.js new file mode 100644 index 0000000..a04bca8 --- /dev/null +++ b/ui/src/api/monitor/operlog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询操作日志列表 +export function list(query) { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }) +} + +// 删除操作日志 +export function delOperlog(operId) { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }) +} + +// 清空操作日志 +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }) +} diff --git a/ui/src/api/system/config.js b/ui/src/api/system/config.js new file mode 100644 index 0000000..02f0cfc --- /dev/null +++ b/ui/src/api/system/config.js @@ -0,0 +1,73 @@ +import request from '@/utils/request' + +// 查询参数列表 +export function listConfig(query) { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }) +} + +// 查询参数详细 +export function getConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'get' + }) +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey) { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }) +} + +// 新增参数配置 +export function addConfig(data) { + return request({ + url: '/system/config', + method: 'post', + data: data + }) +} + +// 修改参数配置 +export function updateConfig(data) { + return request({ + url: '/system/config', + method: 'put', + data: data + }) +} + +// 修改参数配置 +export function updateConfigByKey(key, value) { + return request({ + url: '/system/config/updateByKey', + method: 'put', + data: { + configKey: key, + configValue: value + } + }) +} + +// 删除参数配置 +export function delConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }) +} + +// 刷新参数缓存 +export function refreshCache() { + return request({ + url: '/system/config/refreshCache', + method: 'delete' + }) +} + diff --git a/ui/src/api/system/dept.js b/ui/src/api/system/dept.js new file mode 100644 index 0000000..331c4b2 --- /dev/null +++ b/ui/src/api/system/dept.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询部门列表 +export function listDept(query) { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }) +} + +// 查询部门列表(排除节点) +export function listDeptExcludeChild(deptId) { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }) +} + +// 查询部门详细 +export function getDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }) +} + +// 新增部门 +export function addDept(data) { + return request({ + url: '/system/dept', + method: 'post', + data: data + }) +} + +// 修改部门 +export function updateDept(data) { + return request({ + url: '/system/dept', + method: 'put', + data: data + }) +} + +// 删除部门 +export function delDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }) +} diff --git a/ui/src/api/system/dict/data.js b/ui/src/api/system/dict/data.js new file mode 100644 index 0000000..6c9eb79 --- /dev/null +++ b/ui/src/api/system/dict/data.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询字典数据列表 +export function listData(query) { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }) +} + +// 查询字典数据详细 +export function getData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }) +} + +// 根据字典类型查询字典数据信息 +export function getDicts(dictType) { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }) +} + +// 新增字典数据 +export function addData(data) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }) +} + +// 修改字典数据 +export function updateData(data) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }) +} + +// 删除字典数据 +export function delData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }) +} diff --git a/ui/src/api/system/dict/type.js b/ui/src/api/system/dict/type.js new file mode 100644 index 0000000..a0254ba --- /dev/null +++ b/ui/src/api/system/dict/type.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询字典类型列表 +export function listType(query) { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }) +} + +// 查询字典类型详细 +export function getType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }) +} + +// 新增字典类型 +export function addType(data) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }) +} + +// 修改字典类型 +export function updateType(data) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }) +} + +// 删除字典类型 +export function delType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }) +} + +// 刷新字典缓存 +export function refreshCache() { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete' + }) +} + +// 获取字典选择框列表 +export function optionselect() { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }) +} diff --git a/ui/src/api/system/menu.js b/ui/src/api/system/menu.js new file mode 100644 index 0000000..f6415c6 --- /dev/null +++ b/ui/src/api/system/menu.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询菜单列表 +export function listMenu(query) { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }) +} + +// 查询菜单详细 +export function getMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }) +} + +// 查询菜单下拉树结构 +export function treeselect() { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }) +} + +// 根据角色ID查询菜单下拉树结构 +export function roleMenuTreeselect(roleId) { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }) +} + +// 新增菜单 +export function addMenu(data) { + return request({ + url: '/system/menu', + method: 'post', + data: data + }) +} + +// 修改菜单 +export function updateMenu(data) { + return request({ + url: '/system/menu', + method: 'put', + data: data + }) +} + +// 删除菜单 +export function delMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ui/src/api/system/notice.js b/ui/src/api/system/notice.js new file mode 100644 index 0000000..c274ea5 --- /dev/null +++ b/ui/src/api/system/notice.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询公告列表 +export function listNotice(query) { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }) +} + +// 查询公告详细 +export function getNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }) +} + +// 新增公告 +export function addNotice(data) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }) +} + +// 修改公告 +export function updateNotice(data) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }) +} + +// 删除公告 +export function delNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ui/src/api/system/oss.js b/ui/src/api/system/oss.js new file mode 100644 index 0000000..7d80026 --- /dev/null +++ b/ui/src/api/system/oss.js @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +// 查询OSS对象存储列表 +export function listOss(query) { + return request({ + url: '/system/oss/list', + method: 'get', + params: query + }) +} + +// 查询OSS对象基于id串 +export function listByIds(ossId) { + return request({ + url: '/system/oss/listByIds/' + ossId, + method: 'get' + }) +} + +// 删除OSS对象存储 +export function delOss(ossId) { + return request({ + url: '/system/oss/' + ossId, + method: 'delete' + }) +} + diff --git a/ui/src/api/system/ossConfig.js b/ui/src/api/system/ossConfig.js new file mode 100644 index 0000000..f290762 --- /dev/null +++ b/ui/src/api/system/ossConfig.js @@ -0,0 +1,58 @@ +import request from '@/utils/request' + +// 查询对象存储配置列表 +export function listOssConfig(query) { + return request({ + url: '/system/oss/config/list', + method: 'get', + params: query + }) +} + +// 查询对象存储配置详细 +export function getOssConfig(ossConfigId) { + return request({ + url: '/system/oss/config/' + ossConfigId, + method: 'get' + }) +} + +// 新增对象存储配置 +export function addOssConfig(data) { + return request({ + url: '/system/oss/config', + method: 'post', + data: data + }) +} + +// 修改对象存储配置 +export function updateOssConfig(data) { + return request({ + url: '/system/oss/config', + method: 'put', + data: data + }) +} + +// 删除对象存储配置 +export function delOssConfig(ossConfigId) { + return request({ + url: '/system/oss/config/' + ossConfigId, + method: 'delete' + }) +} + +// 对象存储状态修改 +export function changeOssConfigStatus(ossConfigId, status, configKey) { + const data = { + ossConfigId, + status, + configKey + } + return request({ + url: '/system/oss/config/changeStatus', + method: 'put', + data: data + }) +} diff --git a/ui/src/api/system/post.js b/ui/src/api/system/post.js new file mode 100644 index 0000000..1a8e9ca --- /dev/null +++ b/ui/src/api/system/post.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询岗位列表 +export function listPost(query) { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }) +} + +// 查询岗位详细 +export function getPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'get' + }) +} + +// 新增岗位 +export function addPost(data) { + return request({ + url: '/system/post', + method: 'post', + data: data + }) +} + +// 修改岗位 +export function updatePost(data) { + return request({ + url: '/system/post', + method: 'put', + data: data + }) +} + +// 删除岗位 +export function delPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }) +} diff --git a/ui/src/api/system/role.js b/ui/src/api/system/role.js new file mode 100644 index 0000000..f13e6f4 --- /dev/null +++ b/ui/src/api/system/role.js @@ -0,0 +1,119 @@ +import request from '@/utils/request' + +// 查询角色列表 +export function listRole(query) { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }) +} + +// 查询角色详细 +export function getRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }) +} + +// 新增角色 +export function addRole(data) { + return request({ + url: '/system/role', + method: 'post', + data: data + }) +} + +// 修改角色 +export function updateRole(data) { + return request({ + url: '/system/role', + method: 'put', + data: data + }) +} + +// 角色数据权限 +export function dataScope(data) { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }) +} + +// 角色状态修改 +export function changeRoleStatus(roleId, status) { + const data = { + roleId, + status + } + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }) +} + +// 删除角色 +export function delRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }) +} + +// 查询角色已授权用户列表 +export function allocatedUserList(query) { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query + }) +} + +// 查询角色未授权用户列表 +export function unallocatedUserList(query) { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query + }) +} + +// 取消用户授权角色 +export function authUserCancel(data) { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data + }) +} + +// 批量取消用户授权角色 +export function authUserCancelAll(data) { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data + }) +} + +// 授权用户选择 +export function authUserSelectAll(data) { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data + }) +} + +// 根据角色ID查询部门树结构 +export function deptTreeSelect(roleId) { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get' + }) +} diff --git a/ui/src/api/system/user.js b/ui/src/api/system/user.js new file mode 100644 index 0000000..f2f76ef --- /dev/null +++ b/ui/src/api/system/user.js @@ -0,0 +1,135 @@ +import request from '@/utils/request' +import { parseStrEmpty } from "@/utils/ruoyi"; + +// 查询用户列表 +export function listUser(query) { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }) +} + +// 查询用户详细 +export function getUser(userId) { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get' + }) +} + +// 新增用户 +export function addUser(data) { + return request({ + url: '/system/user', + method: 'post', + data: data + }) +} + +// 修改用户 +export function updateUser(data) { + return request({ + url: '/system/user', + method: 'put', + data: data + }) +} + +// 删除用户 +export function delUser(userId) { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }) +} + +// 用户密码重置 +export function resetUserPwd(userId, password) { + const data = { + userId, + password + } + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data + }) +} + +// 用户状态修改 +export function changeUserStatus(userId, status) { + const data = { + userId, + status + } + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }) +} + +// 查询用户个人信息 +export function getUserProfile() { + return request({ + url: '/system/user/profile', + method: 'get' + }) +} + +// 修改用户个人信息 +export function updateUserProfile(data) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) +} + +// 用户密码重置 +export function updateUserPwd(oldPassword, newPassword) { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + params: data + }) +} + +// 用户头像上传 +export function uploadAvatar(data) { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + data: data + }) +} + +// 查询授权角色 +export function getAuthRole(userId) { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get' + }) +} + +// 保存授权角色 +export function updateAuthRole(data) { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data + }) +} + +// 查询部门下拉树结构 +export function deptTreeSelect() { + return request({ + url: '/system/user/deptTree', + method: 'get' + }) +} diff --git a/ui/src/api/tool/gen.js b/ui/src/api/tool/gen.js new file mode 100644 index 0000000..65ddfea --- /dev/null +++ b/ui/src/api/tool/gen.js @@ -0,0 +1,86 @@ +import request from '@/utils/request' + +// 查询生成表数据 +export function listTable(query) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen/list', + method: 'get', + params: query + }) +} + +// 查询db数据库列表 +export function listDbTable(query) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen/db/list', + method: 'get', + params: query + }) +} + +// 查询表详细信息 +export function getGenTable(tableId) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen/' + tableId, + method: 'get' + }) +} + +// 修改代码生成信息 +export function updateGenTable(data) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen', + method: 'put', + data: data + }) +} + +// 导入表 +export function importTable(data) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen/importTable', + method: 'post', + params: data + }) +} + +// 预览生成代码 +export function previewTable(tableId) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen/preview/' + tableId, + method: 'get' + }) +} + +// 删除表数据 +export function delTable(tableId) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen/' + tableId, + method: 'delete' + }) +} + +// 生成代码(自定义路径) +export function genCode(tableName) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen/genCode/' + tableName, + method: 'get' + }) +} + +// 同步数据库 +export function synchDb(tableName) { + return request({ + headers: { 'datasource': localStorage.getItem("dataName") }, + url: '/tool/gen/synchDb/' + tableName, + method: 'get' + }) +} diff --git a/ui/src/assets/401_images/401.gif b/ui/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/ui/src/assets/401_images/401.gif differ diff --git a/ui/src/assets/404_images/404.png b/ui/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/ui/src/assets/404_images/404.png differ diff --git a/ui/src/assets/404_images/404_cloud.png b/ui/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/ui/src/assets/404_images/404_cloud.png differ diff --git a/ui/src/assets/icons/index.js b/ui/src/assets/icons/index.js new file mode 100644 index 0000000..2c6b309 --- /dev/null +++ b/ui/src/assets/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/ui/src/assets/icons/svg/404.svg b/ui/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/ui/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/bug.svg b/ui/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/ui/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/build.svg b/ui/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/ui/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/button.svg b/ui/src/assets/icons/svg/button.svg new file mode 100644 index 0000000..904fddc --- /dev/null +++ b/ui/src/assets/icons/svg/button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/cascader.svg b/ui/src/assets/icons/svg/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/ui/src/assets/icons/svg/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/chart.svg b/ui/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/ui/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/checkbox.svg b/ui/src/assets/icons/svg/checkbox.svg new file mode 100644 index 0000000..013fd3a --- /dev/null +++ b/ui/src/assets/icons/svg/checkbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/clipboard.svg b/ui/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/ui/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/code.svg b/ui/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..5f9c5ab --- /dev/null +++ b/ui/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/color.svg b/ui/src/assets/icons/svg/color.svg new file mode 100644 index 0000000..44a81aa --- /dev/null +++ b/ui/src/assets/icons/svg/color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/component.svg b/ui/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..29c3458 --- /dev/null +++ b/ui/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/dashboard.svg b/ui/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/ui/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/date-range.svg b/ui/src/assets/icons/svg/date-range.svg new file mode 100644 index 0000000..fda571e --- /dev/null +++ b/ui/src/assets/icons/svg/date-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/date.svg b/ui/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..52dc73e --- /dev/null +++ b/ui/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/dict.svg b/ui/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/ui/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/documentation.svg b/ui/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/ui/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/download.svg b/ui/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/ui/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/drag.svg b/ui/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/ui/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/druid.svg b/ui/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/ui/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/edit.svg b/ui/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/ui/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/education.svg b/ui/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/ui/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/email.svg b/ui/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/ui/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/example.svg b/ui/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/ui/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/excel.svg b/ui/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/ui/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/exit-fullscreen.svg b/ui/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/ui/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/eye-open.svg b/ui/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/ui/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/eye.svg b/ui/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/ui/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/form.svg b/ui/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/ui/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/fullscreen.svg b/ui/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/ui/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/github.svg b/ui/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/ui/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/guide.svg b/ui/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/ui/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/icon.svg b/ui/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/ui/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/input.svg b/ui/src/assets/icons/svg/input.svg new file mode 100644 index 0000000..ab91381 --- /dev/null +++ b/ui/src/assets/icons/svg/input.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/international.svg b/ui/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/ui/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/job.svg b/ui/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/ui/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/language.svg b/ui/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/ui/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/link.svg b/ui/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/ui/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/list.svg b/ui/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/ui/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/lock.svg b/ui/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/ui/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/log.svg b/ui/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/ui/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/logininfor.svg b/ui/src/assets/icons/svg/logininfor.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/ui/src/assets/icons/svg/logininfor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/message.svg b/ui/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/ui/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/money.svg b/ui/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/ui/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/monitor.svg b/ui/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/ui/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/nested.svg b/ui/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/ui/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/number.svg b/ui/src/assets/icons/svg/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/ui/src/assets/icons/svg/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/online.svg b/ui/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/ui/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/password.svg b/ui/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/ui/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/pdf.svg b/ui/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/ui/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/people.svg b/ui/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/ui/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/peoples.svg b/ui/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/ui/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/phone.svg b/ui/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/ui/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/post.svg b/ui/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/ui/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/qq.svg b/ui/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/ui/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/question.svg b/ui/src/assets/icons/svg/question.svg new file mode 100644 index 0000000..cf75bd4 --- /dev/null +++ b/ui/src/assets/icons/svg/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/radio.svg b/ui/src/assets/icons/svg/radio.svg new file mode 100644 index 0000000..0cde345 --- /dev/null +++ b/ui/src/assets/icons/svg/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/rate.svg b/ui/src/assets/icons/svg/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/ui/src/assets/icons/svg/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/redis-list.svg b/ui/src/assets/icons/svg/redis-list.svg new file mode 100644 index 0000000..98a15b2 --- /dev/null +++ b/ui/src/assets/icons/svg/redis-list.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/redis.svg b/ui/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/ui/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/row.svg b/ui/src/assets/icons/svg/row.svg new file mode 100644 index 0000000..0780992 --- /dev/null +++ b/ui/src/assets/icons/svg/row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/search.svg b/ui/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/ui/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/select.svg b/ui/src/assets/icons/svg/select.svg new file mode 100644 index 0000000..d628382 --- /dev/null +++ b/ui/src/assets/icons/svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/server.svg b/ui/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..eb287e3 --- /dev/null +++ b/ui/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/shopping.svg b/ui/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/ui/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/size.svg b/ui/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/ui/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/skill.svg b/ui/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/ui/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/slider.svg b/ui/src/assets/icons/svg/slider.svg new file mode 100644 index 0000000..fbe4f39 --- /dev/null +++ b/ui/src/assets/icons/svg/slider.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/star.svg b/ui/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/ui/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/swagger.svg b/ui/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/ui/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/switch.svg b/ui/src/assets/icons/svg/switch.svg new file mode 100644 index 0000000..0ba61e3 --- /dev/null +++ b/ui/src/assets/icons/svg/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/system.svg b/ui/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..5992593 --- /dev/null +++ b/ui/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/tab.svg b/ui/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/ui/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/table.svg b/ui/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/ui/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/textarea.svg b/ui/src/assets/icons/svg/textarea.svg new file mode 100644 index 0000000..2709f29 --- /dev/null +++ b/ui/src/assets/icons/svg/textarea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/theme.svg b/ui/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/ui/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/time-range.svg b/ui/src/assets/icons/svg/time-range.svg new file mode 100644 index 0000000..13c1202 --- /dev/null +++ b/ui/src/assets/icons/svg/time-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/time.svg b/ui/src/assets/icons/svg/time.svg new file mode 100644 index 0000000..b376e32 --- /dev/null +++ b/ui/src/assets/icons/svg/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/tool.svg b/ui/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..48e0e35 --- /dev/null +++ b/ui/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/tree-table.svg b/ui/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/ui/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/tree.svg b/ui/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/ui/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/upload.svg b/ui/src/assets/icons/svg/upload.svg new file mode 100644 index 0000000..bae49c0 --- /dev/null +++ b/ui/src/assets/icons/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/user.svg b/ui/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/ui/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/validCode.svg b/ui/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/ui/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/wechat.svg b/ui/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/ui/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svg/zip.svg b/ui/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/ui/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/icons/svgo.yml b/ui/src/assets/icons/svgo.yml new file mode 100644 index 0000000..d11906a --- /dev/null +++ b/ui/src/assets/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/ui/src/assets/images/dark.svg b/ui/src/assets/images/dark.svg new file mode 100644 index 0000000..f646bd7 --- /dev/null +++ b/ui/src/assets/images/dark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/src/assets/images/light.svg b/ui/src/assets/images/light.svg new file mode 100644 index 0000000..ab7cc08 --- /dev/null +++ b/ui/src/assets/images/light.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/src/assets/images/login-background.jpg b/ui/src/assets/images/login-background.jpg new file mode 100644 index 0000000..8a89eb8 Binary files /dev/null and b/ui/src/assets/images/login-background.jpg differ diff --git a/ui/src/assets/images/profile.jpg b/ui/src/assets/images/profile.jpg new file mode 100644 index 0000000..b3a940b Binary files /dev/null and b/ui/src/assets/images/profile.jpg differ diff --git a/ui/src/assets/logo/logo.png b/ui/src/assets/logo/logo.png new file mode 100644 index 0000000..e263760 Binary files /dev/null and b/ui/src/assets/logo/logo.png differ diff --git a/ui/src/assets/styles/btn.scss b/ui/src/assets/styles/btn.scss new file mode 100644 index 0000000..e6ba1a8 --- /dev/null +++ b/ui/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/ui/src/assets/styles/element-ui.scss b/ui/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..363092a --- /dev/null +++ b/ui/src/assets/styles/element-ui.scss @@ -0,0 +1,92 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} + +.el-menu--collapse + > div + > .el-submenu + > .el-submenu__title + .el-submenu__icon-arrow { + display: none; +} \ No newline at end of file diff --git a/ui/src/assets/styles/element-variables.scss b/ui/src/assets/styles/element-variables.scss new file mode 100644 index 0000000..1615ff2 --- /dev/null +++ b/ui/src/assets/styles/element-variables.scss @@ -0,0 +1,31 @@ +/** +* I think element-ui's default theme color is too light for long-term use. +* So I modified the default color and you can modify it to your liking. +**/ + +/* theme color */ +$--color-primary: #1890ff; +$--color-success: #13ce66; +$--color-warning: #ffba00; +$--color-danger: #ff4949; +// $--color-info: #1E1E1E; + +$--button-font-weight: 400; + +// $--color-text-regular: #1f2d3d; + +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; + +$--table-border: 1px solid #dfe6ec; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "~element-ui/packages/theme-chalk/src/index"; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + theme: $--color-primary; +} diff --git a/ui/src/assets/styles/index.scss b/ui/src/assets/styles/index.scss new file mode 100644 index 0000000..96095ef --- /dev/null +++ b/ui/src/assets/styles/index.scss @@ -0,0 +1,191 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} + +//refine vue-multiselect plugin +.multiselect { + line-height: 16px; +} + +.multiselect--active { + z-index: 1000 !important; +} diff --git a/ui/src/assets/styles/mixin.scss b/ui/src/assets/styles/mixin.scss new file mode 100644 index 0000000..06fa061 --- /dev/null +++ b/ui/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/ui/src/assets/styles/ruoyi.scss b/ui/src/assets/styles/ruoyi.scss new file mode 100644 index 0000000..4e29874 --- /dev/null +++ b/ui/src/assets/styles/ruoyi.scss @@ -0,0 +1,291 @@ +/** +* 通用css样式布局处理 +* Copyright (c) 2019 ruoyi +*/ + +/** 基础通用 **/ +.pt5 { + padding-top: 5px; +} + +.pr5 { + padding-right: 5px; +} + +.pb5 { + padding-bottom: 5px; +} + +.mt5 { + margin-top: 5px; +} + +.mr5 { + margin-right: 5px; +} + +.mb5 { + margin-bottom: 5px; +} + +.mb8 { + margin-bottom: 8px; +} + +.ml5 { + margin-left: 5px; +} + +.mt10 { + margin-top: 10px; +} + +.mr10 { + margin-right: 10px; +} + +.mb10 { + margin-bottom: 10px; +} +.ml10 { + margin-left: 10px; +} + +.mt20 { + margin-top: 20px; +} + +.mr20 { + margin-right: 20px; +} + +.mb20 { + margin-bottom: 20px; +} +.ml20 { + margin-left: 20px; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.el-message-box__status + .el-message-box__message{ + word-break: break-word; +} + +.el-dialog:not(.is-fullscreen) { + margin-top: 6vh !important; +} + +.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body { + overflow: auto; + overflow-x: hidden; + max-height: 70vh; + padding: 10px 20px 0; +} + +.el-table { + .el-table__header-wrapper, .el-table__fixed-header-wrapper { + th { + word-break: break-word; + background-color: #f8f8f9; + color: #515a6e; + height: 40px; + font-size: 13px; + } + } + + .el-table__body-wrapper { + .el-button [class*="el-icon-"] + span { + margin-left: 1px; + } + } +} + +/** 表单布局 **/ +.form-header { + font-size: 15px; + color: #6379bb; + border-bottom: 1px solid #ddd; + margin: 8px 10px 25px 10px; + padding-bottom: 5px +} + +/** 表格布局 **/ +.pagination-container { + position: relative; + height: 25px; + margin-bottom: 10px; + margin-top: 15px; + padding: 10px 20px !important; +} + +/* tree border */ +.tree-border { + margin-top: 5px; + border: 1px solid #e5e6e7; + background: #FFFFFF none; + border-radius: 4px; +} + +.pagination-container .el-pagination { + right: 0; + position: absolute; +} + +@media (max-width: 768px) { + .pagination-container .el-pagination > .el-pagination__jump { + display: none !important; + } + .pagination-container .el-pagination > .el-pagination__sizes { + display: none !important; + } +} + +.el-table .fixed-width .el-button--mini { + padding-left: 0; + padding-right: 0; + width: inherit; +} + +/** 表格更多操作下拉样式 */ +.el-table .el-dropdown-link,.el-table .el-dropdown-selfdefine { + cursor: pointer; + margin-left: 5px; +} + +.el-table .el-dropdown, .el-icon-arrow-down { + font-size: 12px; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + padding-left: 0px; + list-style: none; +} + +.list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px; +} + +.card-box { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 10px; +} + +/* button color */ +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48D1CC; + border-color: #48D1CC; + color: #FFFFFF; +} + +.el-button--cyan { + background-color: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} + +.avatar-upload-preview { + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 200px; + height: 200px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; +} + +/* 拖拽列样式 */ +.sortable-ghost { + opacity: .8; + color: #fff !important; + background: #42b983 !important; +} + +.top-right-btn { + position: relative; + float: right; +} diff --git a/ui/src/assets/styles/sidebar.scss b/ui/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..ed308b8 --- /dev/null +++ b/ui/src/assets/styles/sidebar.scss @@ -0,0 +1,227 @@ +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: $base-sidebar-width; + position: relative; + } + + .sidebarHide { + margin-left: 0!important; + } + + .sidebar-container { + -webkit-transition: width .28s; + transition: width 0.28s; + width: $base-sidebar-width !important; + background-color: $base-menu-background; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35); + box-shadow: 2px 0 6px rgba(0,21,41,.35); + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + .el-menu-item, .el-submenu__title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .is-active > .el-submenu__title { + color: $base-menu-color-active !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $base-sidebar-width !important; + + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .nest-menu .el-submenu>.el-submenu__title, + & .theme-dark .el-submenu .el-menu-item { + background-color: $base-sub-menu-background !important; + + &:hover { + background-color: $base-sub-menu-hover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $base-sidebar-width !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: $base-sidebar-width !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$base-sidebar-width, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/ui/src/assets/styles/transition.scss b/ui/src/assets/styles/transition.scss new file mode 100644 index 0000000..eb49895 --- /dev/null +++ b/ui/src/assets/styles/transition.scss @@ -0,0 +1,53 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform--move, +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-leave-active { + position: absolute; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/ui/src/assets/styles/variables.scss b/ui/src/assets/styles/variables.scss new file mode 100644 index 0000000..34484d4 --- /dev/null +++ b/ui/src/assets/styles/variables.scss @@ -0,0 +1,54 @@ +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// 默认菜单主题风格 +$base-menu-color:#bfcbd9; +$base-menu-color-active:#f4f4f5; +$base-menu-background:#304156; +$base-logo-title-color: #ffffff; + +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-background:#ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background:#1f2d3d; +$base-sub-menu-hover:#001528; + +// 自定义暗色菜单风格 +/** +$base-menu-color:hsla(0,0%,100%,.65); +$base-menu-color-active:#fff; +$base-menu-background:#001529; +$base-logo-title-color: #ffffff; + +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-background:#ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background:#000c17; +$base-sub-menu-hover:#001528; +*/ + +$base-sidebar-width: 200px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuColor: $base-menu-color; + menuLightColor: $base-menu-light-color; + menuColorActive: $base-menu-color-active; + menuBackground: $base-menu-background; + menuLightBackground: $base-menu-light-background; + subMenuBackground: $base-sub-menu-background; + subMenuHover: $base-sub-menu-hover; + sideBarWidth: $base-sidebar-width; + logoTitleColor: $base-logo-title-color; + logoLightTitleColor: $base-logo-light-title-color +} diff --git a/ui/src/components/Breadcrumb/index.vue b/ui/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..1696f54 --- /dev/null +++ b/ui/src/components/Breadcrumb/index.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/ui/src/components/Crontab/day.vue b/ui/src/components/Crontab/day.vue new file mode 100644 index 0000000..fe3eaf0 --- /dev/null +++ b/ui/src/components/Crontab/day.vue @@ -0,0 +1,161 @@ + + + diff --git a/ui/src/components/Crontab/hour.vue b/ui/src/components/Crontab/hour.vue new file mode 100644 index 0000000..4b1f1fc --- /dev/null +++ b/ui/src/components/Crontab/hour.vue @@ -0,0 +1,114 @@ + + + diff --git a/ui/src/components/Crontab/index.vue b/ui/src/components/Crontab/index.vue new file mode 100644 index 0000000..3963df2 --- /dev/null +++ b/ui/src/components/Crontab/index.vue @@ -0,0 +1,430 @@ + + + + diff --git a/ui/src/components/Crontab/min.vue b/ui/src/components/Crontab/min.vue new file mode 100644 index 0000000..43cab90 --- /dev/null +++ b/ui/src/components/Crontab/min.vue @@ -0,0 +1,116 @@ + + + \ No newline at end of file diff --git a/ui/src/components/Crontab/month.vue b/ui/src/components/Crontab/month.vue new file mode 100644 index 0000000..fd0ac38 --- /dev/null +++ b/ui/src/components/Crontab/month.vue @@ -0,0 +1,114 @@ + + + diff --git a/ui/src/components/Crontab/result.vue b/ui/src/components/Crontab/result.vue new file mode 100644 index 0000000..aea6e0e --- /dev/null +++ b/ui/src/components/Crontab/result.vue @@ -0,0 +1,559 @@ + + + diff --git a/ui/src/components/Crontab/second.vue b/ui/src/components/Crontab/second.vue new file mode 100644 index 0000000..e7b7761 --- /dev/null +++ b/ui/src/components/Crontab/second.vue @@ -0,0 +1,117 @@ + + + diff --git a/ui/src/components/Crontab/week.vue b/ui/src/components/Crontab/week.vue new file mode 100644 index 0000000..1cec700 --- /dev/null +++ b/ui/src/components/Crontab/week.vue @@ -0,0 +1,202 @@ + + + diff --git a/ui/src/components/Crontab/year.vue b/ui/src/components/Crontab/year.vue new file mode 100644 index 0000000..5487a6c --- /dev/null +++ b/ui/src/components/Crontab/year.vue @@ -0,0 +1,131 @@ + + + diff --git a/ui/src/components/DictData/index.js b/ui/src/components/DictData/index.js new file mode 100644 index 0000000..7b85d4a --- /dev/null +++ b/ui/src/components/DictData/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import store from '@/store' +import DataDict from '@/utils/dict' +import { getDicts as getDicts } from '@/api/system/dict/data' + +function searchDictByKey(dict, key) { + if (key == null && key == "") { + return null + } + try { + for (let i = 0; i < dict.length; i++) { + if (dict[i].key == key) { + return dict[i].value + } + } + } catch (e) { + return null + } +} + +function install() { + Vue.use(DataDict, { + metas: { + '*': { + labelField: 'dictLabel', + valueField: 'dictValue', + request(dictMeta) { + const storeDict = searchDictByKey(store.getters.dict, dictMeta.type) + if (storeDict) { + return new Promise(resolve => { resolve(storeDict) }) + } else { + return new Promise((resolve, reject) => { + getDicts(dictMeta.type).then(res => { + store.dispatch('dict/setDict', { key: dictMeta.type, value: res.data }) + resolve(res.data) + }).catch(error => { + reject(error) + }) + }) + } + }, + }, + }, + }) +} + +export default { + install, +} \ No newline at end of file diff --git a/ui/src/components/DictTag/index.vue b/ui/src/components/DictTag/index.vue new file mode 100644 index 0000000..4c196c4 --- /dev/null +++ b/ui/src/components/DictTag/index.vue @@ -0,0 +1,52 @@ + + + + \ No newline at end of file diff --git a/ui/src/components/Editor/index.vue b/ui/src/components/Editor/index.vue new file mode 100644 index 0000000..1f2e15e --- /dev/null +++ b/ui/src/components/Editor/index.vue @@ -0,0 +1,272 @@ + + + + + diff --git a/ui/src/components/FileUpload/index.vue b/ui/src/components/FileUpload/index.vue new file mode 100644 index 0000000..2e33c1b --- /dev/null +++ b/ui/src/components/FileUpload/index.vue @@ -0,0 +1,227 @@ + + + + + diff --git a/ui/src/components/Hamburger/index.vue b/ui/src/components/Hamburger/index.vue new file mode 100644 index 0000000..368b002 --- /dev/null +++ b/ui/src/components/Hamburger/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/ui/src/components/HeaderSearch/index.vue b/ui/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..c4d76bb --- /dev/null +++ b/ui/src/components/HeaderSearch/index.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/ui/src/components/IconSelect/index.vue b/ui/src/components/IconSelect/index.vue new file mode 100644 index 0000000..b0ec9fa --- /dev/null +++ b/ui/src/components/IconSelect/index.vue @@ -0,0 +1,68 @@ + + + + + + diff --git a/ui/src/components/IconSelect/requireIcons.js b/ui/src/components/IconSelect/requireIcons.js new file mode 100644 index 0000000..99e5c54 --- /dev/null +++ b/ui/src/components/IconSelect/requireIcons.js @@ -0,0 +1,11 @@ + +const req = require.context('../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const icons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default icons diff --git a/ui/src/components/ImagePreview/index.vue b/ui/src/components/ImagePreview/index.vue new file mode 100644 index 0000000..cd99626 --- /dev/null +++ b/ui/src/components/ImagePreview/index.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/ui/src/components/ImageUpload/index.vue b/ui/src/components/ImageUpload/index.vue new file mode 100644 index 0000000..9c92e86 --- /dev/null +++ b/ui/src/components/ImageUpload/index.vue @@ -0,0 +1,231 @@ + + + + + diff --git a/ui/src/components/Pagination/index.vue b/ui/src/components/Pagination/index.vue new file mode 100644 index 0000000..56f5a6b --- /dev/null +++ b/ui/src/components/Pagination/index.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/ui/src/components/PanThumb/index.vue b/ui/src/components/PanThumb/index.vue new file mode 100644 index 0000000..1bcf417 --- /dev/null +++ b/ui/src/components/PanThumb/index.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/ui/src/components/ParentView/index.vue b/ui/src/components/ParentView/index.vue new file mode 100644 index 0000000..7bf6148 --- /dev/null +++ b/ui/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ + diff --git a/ui/src/components/RightPanel/index.vue b/ui/src/components/RightPanel/index.vue new file mode 100644 index 0000000..5abeecb --- /dev/null +++ b/ui/src/components/RightPanel/index.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/ui/src/components/RightToolbar/index.vue b/ui/src/components/RightToolbar/index.vue new file mode 100644 index 0000000..527e07c --- /dev/null +++ b/ui/src/components/RightToolbar/index.vue @@ -0,0 +1,104 @@ + + + diff --git a/ui/src/components/RuoYi/Doc/index.vue b/ui/src/components/RuoYi/Doc/index.vue new file mode 100644 index 0000000..a60fd21 --- /dev/null +++ b/ui/src/components/RuoYi/Doc/index.vue @@ -0,0 +1,21 @@ + + + diff --git a/ui/src/components/RuoYi/Git/index.vue b/ui/src/components/RuoYi/Git/index.vue new file mode 100644 index 0000000..faecbc1 --- /dev/null +++ b/ui/src/components/RuoYi/Git/index.vue @@ -0,0 +1,21 @@ + + + diff --git a/ui/src/components/Screenfull/index.vue b/ui/src/components/Screenfull/index.vue new file mode 100644 index 0000000..d4e539c --- /dev/null +++ b/ui/src/components/Screenfull/index.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/ui/src/components/SizeSelect/index.vue b/ui/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..069b5de --- /dev/null +++ b/ui/src/components/SizeSelect/index.vue @@ -0,0 +1,56 @@ + + + diff --git a/ui/src/components/SvgIcon/index.vue b/ui/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..e4bf5ad --- /dev/null +++ b/ui/src/components/SvgIcon/index.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/ui/src/components/ThemePicker/index.vue b/ui/src/components/ThemePicker/index.vue new file mode 100644 index 0000000..1714e1f --- /dev/null +++ b/ui/src/components/ThemePicker/index.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/ui/src/components/TopNav/index.vue b/ui/src/components/TopNav/index.vue new file mode 100644 index 0000000..5f0edbe --- /dev/null +++ b/ui/src/components/TopNav/index.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/ui/src/components/iFrame/index.vue b/ui/src/components/iFrame/index.vue new file mode 100644 index 0000000..426857f --- /dev/null +++ b/ui/src/components/iFrame/index.vue @@ -0,0 +1,36 @@ + + + diff --git a/ui/src/layout/components/Navbar.vue b/ui/src/layout/components/Navbar.vue new file mode 100644 index 0000000..9de102c --- /dev/null +++ b/ui/src/layout/components/Navbar.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/ui/src/layout/components/Settings/index.vue b/ui/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..bb3c9ce --- /dev/null +++ b/ui/src/layout/components/Settings/index.vue @@ -0,0 +1,260 @@ + + + + + diff --git a/ui/src/layout/components/Sidebar/FixiOSBug.js b/ui/src/layout/components/Sidebar/FixiOSBug.js new file mode 100644 index 0000000..6823726 --- /dev/null +++ b/ui/src/layout/components/Sidebar/FixiOSBug.js @@ -0,0 +1,25 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/ui/src/layout/components/Sidebar/Item.vue b/ui/src/layout/components/Sidebar/Item.vue new file mode 100644 index 0000000..be3285d --- /dev/null +++ b/ui/src/layout/components/Sidebar/Item.vue @@ -0,0 +1,33 @@ + diff --git a/ui/src/layout/components/Sidebar/Link.vue b/ui/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..8b0bc93 --- /dev/null +++ b/ui/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,43 @@ + + + diff --git a/ui/src/layout/components/Sidebar/Logo.vue b/ui/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..b8d9d6b --- /dev/null +++ b/ui/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/ui/src/layout/components/Sidebar/SidebarItem.vue b/ui/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..4853fbb --- /dev/null +++ b/ui/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,100 @@ + + + diff --git a/ui/src/layout/components/Sidebar/index.vue b/ui/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..51d0839 --- /dev/null +++ b/ui/src/layout/components/Sidebar/index.vue @@ -0,0 +1,57 @@ + + + diff --git a/ui/src/layout/components/TagsView/ScrollPane.vue b/ui/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..bb753a1 --- /dev/null +++ b/ui/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/ui/src/layout/components/TagsView/index.vue b/ui/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..cc98071 --- /dev/null +++ b/ui/src/layout/components/TagsView/index.vue @@ -0,0 +1,332 @@ + + + + + + + diff --git a/ui/src/layout/components/index.js b/ui/src/layout/components/index.js new file mode 100644 index 0000000..104bd3a --- /dev/null +++ b/ui/src/layout/components/index.js @@ -0,0 +1,5 @@ +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as Settings } from './Settings' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/ui/src/layout/index.vue b/ui/src/layout/index.vue new file mode 100644 index 0000000..f048657 --- /dev/null +++ b/ui/src/layout/index.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/ui/src/layout/mixin/ResizeHandler.js b/ui/src/layout/mixin/ResizeHandler.js new file mode 100644 index 0000000..e8d0df8 --- /dev/null +++ b/ui/src/layout/mixin/ResizeHandler.js @@ -0,0 +1,45 @@ +import store from '@/store' + +const { body } = document +const WIDTH = 992 // refer to Bootstrap's responsive design + +export default { + watch: { + $route(route) { + if (this.device === 'mobile' && this.sidebar.opened) { + store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } + }, + beforeMount() { + window.addEventListener('resize', this.$_resizeHandler) + }, + beforeDestroy() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + mounted() { + const isMobile = this.$_isMobile() + if (isMobile) { + store.dispatch('app/toggleDevice', 'mobile') + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_isMobile() { + const rect = body.getBoundingClientRect() + return rect.width - 1 < WIDTH + }, + $_resizeHandler() { + if (!document.hidden) { + const isMobile = this.$_isMobile() + store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') + + if (isMobile) { + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + } + } + } +} diff --git a/ui/src/main.js b/ui/src/main.js new file mode 100644 index 0000000..caf30d7 --- /dev/null +++ b/ui/src/main.js @@ -0,0 +1,90 @@ +import Vue from 'vue' + +import Cookies from 'js-cookie' + +import Element from 'element-ui' +import './assets/styles/element-variables.scss' + +import '@/assets/styles/index.scss' // global css +import '@/assets/styles/ruoyi.scss' // ruoyi css +import App from './App' +import store from './store' +import router from './router' +import directive from './directive' // directive +import plugins from './plugins' // plugins +import { download } from '@/utils/request' + +import './assets/icons' // icon +import './permission' // permission control +import { getDicts } from "@/api/system/dict/data"; +import { getConfigKey, updateConfigByKey } from "@/api/system/config"; +import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"; +// 分页组件 +import Pagination from "@/components/Pagination"; +// 自定义表格工具组件 +import RightToolbar from "@/components/RightToolbar" +// 富文本组件 +import Editor from "@/components/Editor" +// 文件上传组件 +import FileUpload from "@/components/FileUpload" +// 图片上传组件 +import ImageUpload from "@/components/ImageUpload" +// 图片预览组件 +import ImagePreview from "@/components/ImagePreview" +// 字典标签组件 +import DictTag from '@/components/DictTag' +// 头部标签组件 +import VueMeta from 'vue-meta' +// 字典数据组件 +import DictData from '@/components/DictData' + +// 全局方法挂载 +Vue.prototype.getDicts = getDicts +Vue.prototype.getConfigKey = getConfigKey +Vue.prototype.updateConfigByKey = updateConfigByKey +Vue.prototype.parseTime = parseTime +Vue.prototype.resetForm = resetForm +Vue.prototype.addDateRange = addDateRange +Vue.prototype.selectDictLabel = selectDictLabel +Vue.prototype.selectDictLabels = selectDictLabels +Vue.prototype.download = download +Vue.prototype.handleTree = handleTree + +// 全局组件挂载 +Vue.component('DictTag', DictTag) +Vue.component('Pagination', Pagination) +Vue.component('RightToolbar', RightToolbar) +Vue.component('Editor', Editor) +Vue.component('FileUpload', FileUpload) +Vue.component('ImageUpload', ImageUpload) +Vue.component('ImagePreview', ImagePreview) + +Vue.use(directive) +Vue.use(plugins) +Vue.use(VueMeta) +DictData.install() + +/** + * If you don't want to use mock-server + * you want to use MockJs for mock api + * you can execute: mockXHR() + * + * Currently MockJs will be used in the production environment, + * please remove it before going online! ! ! + */ + +// 修改 el-dialog 默认点击遮照为不关闭 +Element.Dialog.props.closeOnClickModal.default = false + +Vue.use(Element, { + size: Cookies.get('size') || 'medium' // set element-ui default size +}) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + render: h => h(App) +}) diff --git a/ui/src/permission.js b/ui/src/permission.js new file mode 100644 index 0000000..6bb0a1f --- /dev/null +++ b/ui/src/permission.js @@ -0,0 +1,56 @@ +import router from './router' +import store from './store' +import { Message } from 'element-ui' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { getToken } from '@/utils/auth' +import { isRelogin } from '@/utils/request' + +NProgress.configure({ showSpinner: false }) + +const whiteList = ['/login', '/auth-redirect', '/bind', '/register'] + +router.beforeEach((to, from, next) => { + NProgress.start() + if (getToken()) { + to.meta.title && store.dispatch('settings/setTitle', to.meta.title) + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }) + NProgress.done() + } else { + if (store.getters.roles.length === 0) { + isRelogin.show = true + // 判断当前用户是否已拉取完user_info信息 + store.dispatch('GetInfo').then(() => { + isRelogin.show = false + store.dispatch('GenerateRoutes').then(accessRoutes => { + // 根据roles权限生成可访问的路由表 + router.addRoutes(accessRoutes) // 动态添加可访问路由表 + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 + }) + }).catch(err => { + store.dispatch('LogOut').then(() => { + Message.error(err) + next({ path: '/' }) + }) + }) + } else { + next() + } + } + } else { + // 没有token + if (whiteList.indexOf(to.path) !== -1) { + // 在免登录白名单,直接进入 + next() + } else { + next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 + NProgress.done() + } + } +}) + +router.afterEach(() => { + NProgress.done() +}) diff --git a/ui/src/plugins/auth.js b/ui/src/plugins/auth.js new file mode 100644 index 0000000..6c6bc24 --- /dev/null +++ b/ui/src/plugins/auth.js @@ -0,0 +1,60 @@ +import store from '@/store' + +function authPermission(permission) { + const all_permission = "*:*:*"; + const permissions = store.getters && store.getters.permissions + if (permission && permission.length > 0) { + return permissions.some(v => { + return all_permission === v || v === permission + }) + } else { + return false + } +} + +function authRole(role) { + const super_admin = "admin"; + const roles = store.getters && store.getters.roles + if (role && role.length > 0) { + return roles.some(v => { + return super_admin === v || v === role + }) + } else { + return false + } +} + +export default { + // 验证用户是否具备某权限 + hasPermi(permission) { + return authPermission(permission); + }, + // 验证用户是否含有指定权限,只需包含其中一个 + hasPermiOr(permissions) { + return permissions.some(item => { + return authPermission(item) + }) + }, + // 验证用户是否含有指定权限,必须全部拥有 + hasPermiAnd(permissions) { + return permissions.every(item => { + return authPermission(item) + }) + }, + // 验证用户是否具备某角色 + hasRole(role) { + return authRole(role); + }, + // 验证用户是否含有指定角色,只需包含其中一个 + hasRoleOr(roles) { + return roles.some(item => { + return authRole(item) + }) + }, + // 验证用户是否含有指定角色,必须全部拥有 + hasRoleAnd(roles) { + return roles.every(item => { + return authRole(item) + }) + } +} diff --git a/ui/src/plugins/cache.js b/ui/src/plugins/cache.js new file mode 100644 index 0000000..6b5c00b --- /dev/null +++ b/ui/src/plugins/cache.js @@ -0,0 +1,77 @@ +const sessionCache = { + set (key, value) { + if (!sessionStorage) { + return + } + if (key != null && value != null) { + sessionStorage.setItem(key, value) + } + }, + get (key) { + if (!sessionStorage) { + return null + } + if (key == null) { + return null + } + return sessionStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + }, + remove (key) { + sessionStorage.removeItem(key); + } +} +const localCache = { + set (key, value) { + if (!localStorage) { + return + } + if (key != null && value != null) { + localStorage.setItem(key, value) + } + }, + get (key) { + if (!localStorage) { + return null + } + if (key == null) { + return null + } + return localStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + }, + remove (key) { + localStorage.removeItem(key); + } +} + +export default { + /** + * 会话级缓存 + */ + session: sessionCache, + /** + * 本地缓存 + */ + local: localCache +} diff --git a/ui/src/plugins/download.js b/ui/src/plugins/download.js new file mode 100644 index 0000000..a1b21da --- /dev/null +++ b/ui/src/plugins/download.js @@ -0,0 +1,65 @@ +import axios from 'axios' +import {Loading, Message} from 'element-ui' +import { saveAs } from 'file-saver' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { blobValidate } from "@/utils/ruoyi"; + +const baseURL = process.env.VUE_APP_BASE_API +let downloadLoadingInstance; + +export default { + oss(ossId) { + var url = baseURL + '/system/oss/download/' + ossId + downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then(async (res) => { + const isLogin = await blobValidate(res.data); + if (isLogin) { + const blob = new Blob([res.data], { type: 'application/octet-stream' }) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) + } else { + this.printErrMsg(res.data); + } + downloadLoadingInstance.close(); + }).catch((r) => { + console.error(r) + Message.error('下载文件出现错误,请联系管理员!') + downloadLoadingInstance.close(); + }) + }, + zip(url, name) { + var url = baseURL + url + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { + 'Authorization': 'Bearer ' + getToken(), + 'datasource': localStorage.getItem("dataName") + } + }).then(async (res) => { + const isLogin = await blobValidate(res.data); + if (isLogin) { + const blob = new Blob([res.data], { type: 'application/zip' }) + this.saveAs(blob, name) + } else { + this.printErrMsg(res.data); + } + }) + }, + saveAs(text, name, opts) { + saveAs(text, name, opts); + }, + async printErrMsg(data) { + const resText = await data.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + Message.error(errMsg); + } +} + diff --git a/ui/src/plugins/index.js b/ui/src/plugins/index.js new file mode 100644 index 0000000..d000f2d --- /dev/null +++ b/ui/src/plugins/index.js @@ -0,0 +1,20 @@ +import tab from './tab' +import auth from './auth' +import cache from './cache' +import modal from './modal' +import download from './download' + +export default { + install(Vue) { + // 页签操作 + Vue.prototype.$tab = tab + // 认证对象 + Vue.prototype.$auth = auth + // 缓存对象 + Vue.prototype.$cache = cache + // 模态框对象 + Vue.prototype.$modal = modal + // 下载文件 + Vue.prototype.$download = download + } +} diff --git a/ui/src/plugins/modal.js b/ui/src/plugins/modal.js new file mode 100644 index 0000000..b37ca14 --- /dev/null +++ b/ui/src/plugins/modal.js @@ -0,0 +1,83 @@ +import { Message, MessageBox, Notification, Loading } from 'element-ui' + +let loadingInstance; + +export default { + // 消息提示 + msg(content) { + Message.info(content) + }, + // 错误消息 + msgError(content) { + Message.error(content) + }, + // 成功消息 + msgSuccess(content) { + Message.success(content) + }, + // 警告消息 + msgWarning(content) { + Message.warning(content) + }, + // 弹出提示 + alert(content) { + MessageBox.alert(content, "系统提示") + }, + // 错误提示 + alertError(content) { + MessageBox.alert(content, "系统提示", { type: 'error' }) + }, + // 成功提示 + alertSuccess(content) { + MessageBox.alert(content, "系统提示", { type: 'success' }) + }, + // 警告提示 + alertWarning(content) { + MessageBox.alert(content, "系统提示", { type: 'warning' }) + }, + // 通知提示 + notify(content) { + Notification.info(content) + }, + // 错误通知 + notifyError(content) { + Notification.error(content); + }, + // 成功通知 + notifySuccess(content) { + Notification.success(content) + }, + // 警告通知 + notifyWarning(content) { + Notification.warning(content) + }, + // 确认窗体 + confirm(content) { + return MessageBox.confirm(content, "系统提示", { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: "warning", + }) + }, + // 提交内容 + prompt(content) { + return MessageBox.prompt(content, "系统提示", { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: "warning", + }) + }, + // 打开遮罩层 + loading(content) { + loadingInstance = Loading.service({ + lock: true, + text: content, + spinner: "el-icon-loading", + background: "rgba(0, 0, 0, 0.7)", + }) + }, + // 关闭遮罩层 + closeLoading() { + loadingInstance.close(); + } +} diff --git a/ui/src/plugins/tab.js b/ui/src/plugins/tab.js new file mode 100644 index 0000000..cade9f7 --- /dev/null +++ b/ui/src/plugins/tab.js @@ -0,0 +1,67 @@ +import store from '@/store' +import router from '@/router'; + +export default { + // 刷新当前tab页签 + refreshPage(obj) { + const { path, query, matched } = router.currentRoute; + if (obj === undefined) { + matched.forEach((m) => { + if (m.components && m.components.default && m.components.default.name) { + if (!['Layout', 'ParentView'].includes(m.components.default.name)) { + obj = { name: m.components.default.name, path: path, query: query }; + } + } + }); + } + return store.dispatch('tagsView/delCachedView', obj).then(() => { + const { path, query } = obj + router.replace({ + path: '/redirect' + path, + query: query + }) + }) + }, + // 关闭当前tab页签,打开新页签 + closeOpenPage(obj) { + store.dispatch("tagsView/delView", router.currentRoute); + if (obj !== undefined) { + return router.push(obj); + } + }, + // 关闭指定tab页签 + closePage(obj) { + if (obj === undefined) { + return store.dispatch('tagsView/delView', router.currentRoute).then(({ lastPath }) => { + return router.push(lastPath || '/'); + }); + } + return store.dispatch('tagsView/delView', obj); + }, + // 关闭所有tab页签 + closeAllPage() { + return store.dispatch('tagsView/delAllViews'); + }, + // 关闭左侧tab页签 + closeLeftPage(obj) { + return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute); + }, + // 关闭右侧tab页签 + closeRightPage(obj) { + return store.dispatch('tagsView/delRightTags', obj || router.currentRoute); + }, + // 关闭其他tab页签 + closeOtherPage(obj) { + return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute); + }, + // 添加tab页签 + openPage(title, url, params) { + var obj = { path: url, meta: { title: title } } + store.dispatch('tagsView/addView', obj); + return router.push({ path: url, query: params }); + }, + // 修改tab页签 + updatePage(obj) { + return store.dispatch('tagsView/updateVisitedView', obj); + } +} diff --git a/ui/src/router/index.js b/ui/src/router/index.js new file mode 100644 index 0000000..dd4d5f1 --- /dev/null +++ b/ui/src/router/index.js @@ -0,0 +1,178 @@ +import Vue from 'vue' +import Router from 'vue-router' + +Vue.use(Router) + +/* Layout */ +import Layout from '@/layout' + +/** + * Note: 路由配置项 + * + * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 + * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 + * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 + * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 + * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 + * roles: ['admin', 'common'] // 访问路由的角色权限 + * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 + * meta : { + noCache: true // 如果设置为true,则不会被 缓存(默认 false) + title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 + icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg + breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 + activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 + } + */ + +// 公共路由 +export const constantRoutes = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect') + } + ] + }, + { + path: '/login', + component: () => import('@/views/login'), + hidden: true + }, + { + path: '/register', + component: () => import('@/views/register'), + hidden: true + }, + { + path: '/404', + component: () => import('@/views/error/404'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error/401'), + hidden: true + }, + { + path: '', + component: Layout, + redirect: 'index', + children: [ + { + path: 'index', + component: () => import('@/views/index'), + name: 'Index', + meta: { title: '首页', icon: 'dashboard', affix: true } + } + ] + }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile', + component: () => import('@/views/system/user/profile/index'), + name: 'Profile', + meta: { title: '个人中心', icon: 'user' } + } + ] + } +] + +// 动态路由,基于用户权限动态去加载 +export const dynamicRoutes = [ + { + path: '/system/user-auth', + component: Layout, + hidden: true, + permissions: ['system:user:edit'], + children: [ + { + path: 'role/:userId(\\d+)', + component: () => import('@/views/system/user/authRole'), + name: 'AuthRole', + meta: { title: '分配角色', activeMenu: '/system/user' } + } + ] + }, + { + path: '/system/role-auth', + component: Layout, + hidden: true, + permissions: ['system:role:edit'], + children: [ + { + path: 'user/:roleId(\\d+)', + component: () => import('@/views/system/role/authUser'), + name: 'AuthUser', + meta: { title: '分配用户', activeMenu: '/system/role' } + } + ] + }, + { + path: '/system/dict-data', + component: Layout, + hidden: true, + permissions: ['system:dict:list'], + children: [ + { + path: 'index/:dictId(\\d+)', + component: () => import('@/views/system/dict/data'), + name: 'Data', + meta: { title: '字典数据', activeMenu: '/system/dict' } + } + ] + }, + { + path: '/system/oss-config', + component: Layout, + hidden: true, + permissions: ['system:oss:list'], + children: [ + { + path: 'index', + component: () => import('@/views/system/oss/config'), + name: 'OssConfig', + meta: { title: '配置管理', activeMenu: '/system/oss' } + } + ] + }, + { + path: '/tool/gen-edit', + component: Layout, + hidden: true, + permissions: ['tool:gen:edit'], + children: [ + { + path: 'index/:tableId(\\d+)', + component: () => import('@/views/tool/gen/editTable'), + name: 'GenEdit', + meta: { title: '修改生成配置', activeMenu: '/tool/gen' } + } + ] + } +] + +// 防止连续点击多次路由报错 +let routerPush = Router.prototype.push; +Router.prototype.push = function push(location) { + return routerPush.call(this, location).catch(err => err) +} + +export default new Router({ + base: process.env.VUE_APP_CONTEXT_PATH, + mode: 'history', // 去掉url中的# + scrollBehavior: () => ({ y: 0 }), + routes: constantRoutes +}) diff --git a/ui/src/settings.js b/ui/src/settings.js new file mode 100644 index 0000000..6a0b09f --- /dev/null +++ b/ui/src/settings.js @@ -0,0 +1,44 @@ +module.exports = { + /** + * 侧边栏主题 深色主题theme-dark,浅色主题theme-light + */ + sideTheme: 'theme-dark', + + /** + * 是否系统布局配置 + */ + showSettings: false, + + /** + * 是否显示顶部导航 + */ + topNav: false, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 是否固定头部 + */ + fixedHeader: false, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * 是否显示动态标题 + */ + dynamicTitle: false, + + /** + * @type {string | array} 'production' | ['production', 'development'] + * @description Need show err logs component. + * The default is only used in the production env + * If you want to also use it in dev, you can pass ['production', 'development'] + */ + errorLog: 'production' +} diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js new file mode 100644 index 0000000..8adb1b6 --- /dev/null +++ b/ui/src/store/getters.js @@ -0,0 +1,19 @@ +const getters = { + sidebar: state => state.app.sidebar, + size: state => state.app.size, + device: state => state.app.device, + dict: state => state.dict.dict, + visitedViews: state => state.tagsView.visitedViews, + cachedViews: state => state.tagsView.cachedViews, + token: state => state.user.token, + avatar: state => state.user.avatar, + name: state => state.user.name, + introduction: state => state.user.introduction, + roles: state => state.user.roles, + permissions: state => state.user.permissions, + permission_routes: state => state.permission.routes, + topbarRouters:state => state.permission.topbarRouters, + defaultRoutes:state => state.permission.defaultRoutes, + sidebarRouters:state => state.permission.sidebarRouters, +} +export default getters diff --git a/ui/src/store/index.js b/ui/src/store/index.js new file mode 100644 index 0000000..97aaef8 --- /dev/null +++ b/ui/src/store/index.js @@ -0,0 +1,25 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import app from './modules/app' +import dict from './modules/dict' +import user from './modules/user' +import tagsView from './modules/tagsView' +import permission from './modules/permission' +import settings from './modules/settings' +import getters from './getters' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + modules: { + app, + dict, + user, + tagsView, + permission, + settings + }, + getters +}) + +export default store diff --git a/ui/src/store/modules/app.js b/ui/src/store/modules/app.js new file mode 100644 index 0000000..3e22d1c --- /dev/null +++ b/ui/src/store/modules/app.js @@ -0,0 +1,66 @@ +import Cookies from 'js-cookie' + +const state = { + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, + withoutAnimation: false, + hide: false + }, + device: 'desktop', + size: Cookies.get('size') || 'medium' +} + +const mutations = { + TOGGLE_SIDEBAR: state => { + if (state.sidebar.hide) { + return false; + } + state.sidebar.opened = !state.sidebar.opened + state.sidebar.withoutAnimation = false + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + }, + CLOSE_SIDEBAR: (state, withoutAnimation) => { + Cookies.set('sidebarStatus', 0) + state.sidebar.opened = false + state.sidebar.withoutAnimation = withoutAnimation + }, + TOGGLE_DEVICE: (state, device) => { + state.device = device + }, + SET_SIZE: (state, size) => { + state.size = size + Cookies.set('size', size) + }, + SET_SIDEBAR_HIDE: (state, status) => { + state.sidebar.hide = status + } +} + +const actions = { + toggleSideBar({ commit }) { + commit('TOGGLE_SIDEBAR') + }, + closeSideBar({ commit }, { withoutAnimation }) { + commit('CLOSE_SIDEBAR', withoutAnimation) + }, + toggleDevice({ commit }, device) { + commit('TOGGLE_DEVICE', device) + }, + setSize({ commit }, size) { + commit('SET_SIZE', size) + }, + toggleSideBarHide({ commit }, status) { + commit('SET_SIDEBAR_HIDE', status) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ui/src/store/modules/dict.js b/ui/src/store/modules/dict.js new file mode 100644 index 0000000..f95bead --- /dev/null +++ b/ui/src/store/modules/dict.js @@ -0,0 +1,50 @@ +const state = { + dict: new Array() +} +const mutations = { + SET_DICT: (state, { key, value }) => { + if (key !== null && key !== "") { + state.dict.push({ + key: key, + value: value + }) + } + }, + REMOVE_DICT: (state, key) => { + try { + for (let i = 0; i < state.dict.length; i++) { + if (state.dict[i].key == key) { + state.dict.splice(i, i) + return true + } + } + } catch (e) { + } + }, + CLEAN_DICT: (state) => { + state.dict = new Array() + } +} + +const actions = { + // 设置字典 + setDict({ commit }, data) { + commit('SET_DICT', data) + }, + // 删除字典 + removeDict({ commit }, key) { + commit('REMOVE_DICT', key) + }, + // 清空字典 + cleanDict({ commit }) { + commit('CLEAN_DICT') + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/ui/src/store/modules/permission.js b/ui/src/store/modules/permission.js new file mode 100644 index 0000000..2287665 --- /dev/null +++ b/ui/src/store/modules/permission.js @@ -0,0 +1,133 @@ +import auth from '@/plugins/auth' +import router, { constantRoutes, dynamicRoutes } from '@/router' +import { getRouters } from '@/api/menu' +import Layout from '@/layout/index' +import ParentView from '@/components/ParentView' +import InnerLink from '@/layout/components/InnerLink' + +const permission = { + state: { + routes: [], + addRoutes: [], + defaultRoutes: [], + topbarRouters: [], + sidebarRouters: [] + }, + mutations: { + SET_ROUTES: (state, routes) => { + state.addRoutes = routes + state.routes = constantRoutes.concat(routes) + }, + SET_DEFAULT_ROUTES: (state, routes) => { + state.defaultRoutes = constantRoutes.concat(routes) + }, + SET_TOPBAR_ROUTES: (state, routes) => { + state.topbarRouters = routes + }, + SET_SIDEBAR_ROUTERS: (state, routes) => { + state.sidebarRouters = routes + }, + }, + actions: { + // 生成路由 + GenerateRoutes({ commit }) { + return new Promise(resolve => { + // 向后端请求路由数据 + getRouters().then(res => { + const sdata = JSON.parse(JSON.stringify(res.data)) + const rdata = JSON.parse(JSON.stringify(res.data)) + const sidebarRoutes = filterAsyncRouter(sdata) + const rewriteRoutes = filterAsyncRouter(rdata, false, true) + const asyncRoutes = filterDynamicRoutes(dynamicRoutes); + rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true }) + router.addRoutes(asyncRoutes); + commit('SET_ROUTES', rewriteRoutes) + commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes)) + commit('SET_DEFAULT_ROUTES', sidebarRoutes) + commit('SET_TOPBAR_ROUTES', sidebarRoutes) + resolve(rewriteRoutes) + }) + }) + } + } +} + +// 遍历后台传来的路由字符串,转换为组件对象 +function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { + return asyncRouterMap.filter(route => { + if (type && route.children) { + route.children = filterChildren(route.children) + } + if (route.component) { + // Layout ParentView 组件特殊处理 + if (route.component === 'Layout') { + route.component = Layout + } else if (route.component === 'ParentView') { + route.component = ParentView + } else if (route.component === 'InnerLink') { + route.component = InnerLink + } else { + route.component = loadView(route.component) + } + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type) + } else { + delete route['children'] + delete route['redirect'] + } + return true + }) +} + +function filterChildren(childrenMap, lastRouter = false) { + var children = [] + childrenMap.forEach((el, index) => { + if (el.children && el.children.length) { + if (el.component === 'ParentView' && !lastRouter) { + el.children.forEach(c => { + c.path = el.path + '/' + c.path + if (c.children && c.children.length) { + children = children.concat(filterChildren(c.children, c)) + return + } + children.push(c) + }) + return + } + } + if (lastRouter) { + el.path = lastRouter.path + '/' + el.path + } + children = children.concat(el) + }) + return children +} + +// 动态路由遍历,验证是否具备权限 +export function filterDynamicRoutes(routes) { + const res = [] + routes.forEach(route => { + if (route.permissions) { + if (auth.hasPermiOr(route.permissions)) { + res.push(route) + } + } else if (route.roles) { + if (auth.hasRoleOr(route.roles)) { + res.push(route) + } + } + }) + return res +} + +export const loadView = (view) => { + if (process.env.NODE_ENV === 'development') { + return (resolve) => require([`@/views/${view}`], resolve) + } else { + // 使用 import 实现生产环境的路由懒加载 + return () => import(`@/views/${view}`) + } +} + +export default permission diff --git a/ui/src/store/modules/settings.js b/ui/src/store/modules/settings.js new file mode 100644 index 0000000..2455a1e --- /dev/null +++ b/ui/src/store/modules/settings.js @@ -0,0 +1,42 @@ +import defaultSettings from '@/settings' + +const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings + +const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' +const state = { + title: '', + theme: storageSetting.theme || '#409EFF', + sideTheme: storageSetting.sideTheme || sideTheme, + showSettings: showSettings, + topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav, + tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, + fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, + sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo, + dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle +} +const mutations = { + CHANGE_SETTING: (state, { key, value }) => { + if (state.hasOwnProperty(key)) { + state[key] = value + } + } +} + +const actions = { + // 修改布局设置 + changeSetting({ commit }, data) { + commit('CHANGE_SETTING', data) + }, + // 设置网页标题 + setTitle({ commit }, title) { + state.title = title + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/ui/src/store/modules/tagsView.js b/ui/src/store/modules/tagsView.js new file mode 100644 index 0000000..5fc011c --- /dev/null +++ b/ui/src/store/modules/tagsView.js @@ -0,0 +1,228 @@ +const state = { + visitedViews: [], + cachedViews: [], + iframeViews: [] +} + +const mutations = { + ADD_IFRAME_VIEW: (state, view) => { + if (state.iframeViews.some(v => v.path === view.path)) return + state.iframeViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_VISITED_VIEW: (state, view) => { + if (state.visitedViews.some(v => v.path === view.path)) return + state.visitedViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_CACHED_VIEW: (state, view) => { + if (state.cachedViews.includes(view.name)) return + if (view.meta && !view.meta.noCache) { + state.cachedViews.push(view.name) + } + }, + DEL_VISITED_VIEW: (state, view) => { + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + state.visitedViews.splice(i, 1) + break + } + } + state.iframeViews = state.iframeViews.filter(item => item.path !== view.path) + }, + DEL_IFRAME_VIEW: (state, view) => { + state.iframeViews = state.iframeViews.filter(item => item.path !== view.path) + }, + DEL_CACHED_VIEW: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + index > -1 && state.cachedViews.splice(index, 1) + }, + + DEL_OTHERS_VISITED_VIEWS: (state, view) => { + state.visitedViews = state.visitedViews.filter(v => { + return v.meta.affix || v.path === view.path + }) + state.iframeViews = state.iframeViews.filter(item => item.path === view.path) + }, + DEL_OTHERS_CACHED_VIEWS: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + if (index > -1) { + state.cachedViews = state.cachedViews.slice(index, index + 1) + } else { + state.cachedViews = [] + } + }, + DEL_ALL_VISITED_VIEWS: state => { + // keep affix tags + const affixTags = state.visitedViews.filter(tag => tag.meta.affix) + state.visitedViews = affixTags + state.iframeViews = [] + }, + DEL_ALL_CACHED_VIEWS: state => { + state.cachedViews = [] + }, + UPDATE_VISITED_VIEW: (state, view) => { + for (let v of state.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + }, + DEL_RIGHT_VIEWS: (state, view) => { + const index = state.visitedViews.findIndex(v => v.path === view.path) + if (index === -1) { + return + } + state.visitedViews = state.visitedViews.filter((item, idx) => { + if (idx <= index || (item.meta && item.meta.affix)) { + return true + } + const i = state.cachedViews.indexOf(item.name) + if (i > -1) { + state.cachedViews.splice(i, 1) + } + if(item.meta.link) { + const fi = state.iframeViews.findIndex(v => v.path === item.path) + state.iframeViews.splice(fi, 1) + } + return false + }) + }, + DEL_LEFT_VIEWS: (state, view) => { + const index = state.visitedViews.findIndex(v => v.path === view.path) + if (index === -1) { + return + } + state.visitedViews = state.visitedViews.filter((item, idx) => { + if (idx >= index || (item.meta && item.meta.affix)) { + return true + } + const i = state.cachedViews.indexOf(item.name) + if (i > -1) { + state.cachedViews.splice(i, 1) + } + if(item.meta.link) { + const fi = state.iframeViews.findIndex(v => v.path === item.path) + state.iframeViews.splice(fi, 1) + } + return false + }) + } +} + +const actions = { + addView({ dispatch }, view) { + dispatch('addVisitedView', view) + dispatch('addCachedView', view) + }, + addIframeView({ commit }, view) { + commit('ADD_IFRAME_VIEW', view) + }, + addVisitedView({ commit }, view) { + commit('ADD_VISITED_VIEW', view) + }, + addCachedView({ commit }, view) { + commit('ADD_CACHED_VIEW', view) + }, + delView({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delVisitedView', view) + dispatch('delCachedView', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delVisitedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_VISITED_VIEW', view) + resolve([...state.visitedViews]) + }) + }, + delIframeView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_IFRAME_VIEW', view) + resolve([...state.iframeViews]) + }) + }, + delCachedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_CACHED_VIEW', view) + resolve([...state.cachedViews]) + }) + }, + delOthersViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delOthersVisitedViews', view) + dispatch('delOthersCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delOthersVisitedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_VISITED_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delOthersCachedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_CACHED_VIEWS', view) + resolve([...state.cachedViews]) + }) + }, + delAllViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delAllVisitedViews', view) + dispatch('delAllCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delAllVisitedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_VISITED_VIEWS') + resolve([...state.visitedViews]) + }) + }, + delAllCachedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_CACHED_VIEWS') + resolve([...state.cachedViews]) + }) + }, + updateVisitedView({ commit }, view) { + commit('UPDATE_VISITED_VIEW', view) + }, + delRightTags({ commit }, view) { + return new Promise(resolve => { + commit('DEL_RIGHT_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delLeftTags({ commit }, view) { + return new Promise(resolve => { + commit('DEL_LEFT_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js new file mode 100644 index 0000000..d67c9a4 --- /dev/null +++ b/ui/src/store/modules/user.js @@ -0,0 +1,96 @@ +import { login, logout, getInfo } from '@/api/login' +import { getToken, setToken, removeToken } from '@/utils/auth' + +const user = { + state: { + token: getToken(), + name: '', + avatar: '', + roles: [], + permissions: [] + }, + + mutations: { + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles + }, + SET_PERMISSIONS: (state, permissions) => { + state.permissions = permissions + } + }, + + actions: { + // 登录 + Login({ commit }, userInfo) { + const username = userInfo.username.trim() + const password = userInfo.password + const code = userInfo.code + const uuid = userInfo.uuid + return new Promise((resolve, reject) => { + login(username, password, code, uuid).then(res => { + setToken(res.data.token) + commit('SET_TOKEN', res.data.token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 获取用户信息 + GetInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getInfo().then(res => { + const user = res.data.user + const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : user.avatar; + if (res.data.roles && res.data.roles.length > 0) { // 验证返回的roles是否是一个非空数组 + commit('SET_ROLES', res.data.roles) + commit('SET_PERMISSIONS', res.data.permissions) + } else { + commit('SET_ROLES', ['ROLE_DEFAULT']) + } + commit('SET_NAME', user.userName) + commit('SET_AVATAR', avatar) + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }, + + // 退出系统 + LogOut({ commit, state }) { + return new Promise((resolve, reject) => { + logout(state.token).then(() => { + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + commit('SET_PERMISSIONS', []) + removeToken() + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 前端 登出 + FedLogOut({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + } + } +} + +export default user diff --git a/ui/src/utils/auth.js b/ui/src/utils/auth.js new file mode 100644 index 0000000..08a43d6 --- /dev/null +++ b/ui/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/ui/src/utils/dict/Dict.js b/ui/src/utils/dict/Dict.js new file mode 100644 index 0000000..104bd6e --- /dev/null +++ b/ui/src/utils/dict/Dict.js @@ -0,0 +1,82 @@ +import Vue from 'vue' +import { mergeRecursive } from "@/utils/ruoyi"; +import DictMeta from './DictMeta' +import DictData from './DictData' + +const DEFAULT_DICT_OPTIONS = { + types: [], +} + +/** + * @classdesc 字典 + * @property {Object} label 标签对象,内部属性名为字典类型名称 + * @property {Object} dict 字段数组,内部属性名为字典类型名称 + * @property {Array.} _dictMetas 字典元数据数组 + */ +export default class Dict { + constructor() { + this.owner = null + this.label = {} + this.type = {} + } + + init(options) { + if (options instanceof Array) { + options = { types: options } + } + const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options) + if (opts.types === undefined) { + throw new Error('need dict types') + } + const ps = [] + this._dictMetas = opts.types.map(t => DictMeta.parse(t)) + this._dictMetas.forEach(dictMeta => { + const type = dictMeta.type + Vue.set(this.label, type, {}) + Vue.set(this.type, type, []) + if (dictMeta.lazy) { + return + } + ps.push(loadDict(this, dictMeta)) + }) + return Promise.all(ps) + } + + /** + * 重新加载字典 + * @param {String} type 字典类型 + */ + reloadDict(type) { + const dictMeta = this._dictMetas.find(e => e.type === type) + if (dictMeta === undefined) { + return Promise.reject(`the dict meta of ${type} was not found`) + } + return loadDict(this, dictMeta) + } +} + +/** + * 加载字典 + * @param {Dict} dict 字典 + * @param {DictMeta} dictMeta 字典元数据 + * @returns {Promise} + */ +function loadDict(dict, dictMeta) { + return dictMeta.request(dictMeta) + .then(response => { + const type = dictMeta.type + let dicts = dictMeta.responseConverter(response, dictMeta) + if (!(dicts instanceof Array)) { + console.error('the return of responseConverter must be Array.') + dicts = [] + } else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) { + console.error('the type of elements in dicts must be DictData') + dicts = [] + } + dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts) + dicts.forEach(d => { + Vue.set(dict.label[type], d.value, d.label) + }) + return dicts + }) +} diff --git a/ui/src/utils/dict/DictConverter.js b/ui/src/utils/dict/DictConverter.js new file mode 100644 index 0000000..0cf5df8 --- /dev/null +++ b/ui/src/utils/dict/DictConverter.js @@ -0,0 +1,17 @@ +import DictOptions from './DictOptions' +import DictData from './DictData' + +export default function(dict, dictMeta) { + const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS) + const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS) + return new DictData(dict[label], dict[value], dict) +} + +/** + * 确定字典字段 + * @param {DictData} dict + * @param {...String} fields + */ +function determineDictField(dict, ...fields) { + return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f)) +} diff --git a/ui/src/utils/dict/DictData.js b/ui/src/utils/dict/DictData.js new file mode 100644 index 0000000..afc763e --- /dev/null +++ b/ui/src/utils/dict/DictData.js @@ -0,0 +1,13 @@ +/** + * @classdesc 字典数据 + * @property {String} label 标签 + * @property {*} value 标签 + * @property {Object} raw 原始数据 + */ +export default class DictData { + constructor(label, value, raw) { + this.label = label + this.value = value + this.raw = raw + } +} diff --git a/ui/src/utils/dict/DictMeta.js b/ui/src/utils/dict/DictMeta.js new file mode 100644 index 0000000..9779daa --- /dev/null +++ b/ui/src/utils/dict/DictMeta.js @@ -0,0 +1,38 @@ +import { mergeRecursive } from "@/utils/ruoyi"; +import DictOptions from './DictOptions' + +/** + * @classdesc 字典元数据 + * @property {String} type 类型 + * @property {Function} request 请求 + * @property {String} label 标签字段 + * @property {String} value 值字段 + */ +export default class DictMeta { + constructor(options) { + this.type = options.type + this.request = options.request + this.responseConverter = options.responseConverter + this.labelField = options.labelField + this.valueField = options.valueField + this.lazy = options.lazy === true + } +} + + +/** + * 解析字典元数据 + * @param {Object} options + * @returns {DictMeta} + */ +DictMeta.parse= function(options) { + let opts = null + if (typeof options === 'string') { + opts = DictOptions.metas[options] || {} + opts.type = options + } else if (typeof options === 'object') { + opts = options + } + opts = mergeRecursive(DictOptions.metas['*'], opts) + return new DictMeta(opts) +} diff --git a/ui/src/utils/dict/DictOptions.js b/ui/src/utils/dict/DictOptions.js new file mode 100644 index 0000000..338a94e --- /dev/null +++ b/ui/src/utils/dict/DictOptions.js @@ -0,0 +1,51 @@ +import { mergeRecursive } from "@/utils/ruoyi"; +import dictConverter from './DictConverter' + +export const options = { + metas: { + '*': { + /** + * 字典请求,方法签名为function(dictMeta: DictMeta): Promise + */ + request: (dictMeta) => { + console.log(`load dict ${dictMeta.type}`) + return Promise.resolve([]) + }, + /** + * 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData + */ + responseConverter, + labelField: 'label', + valueField: 'value', + }, + }, + /** + * 默认标签字段 + */ + DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'], + /** + * 默认值字段 + */ + DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'], +} + +/** + * 映射字典 + * @param {Object} response 字典数据 + * @param {DictMeta} dictMeta 字典元数据 + * @returns {DictData} + */ +function responseConverter(response, dictMeta) { + const dicts = response.content instanceof Array ? response.content : response + if (dicts === undefined) { + console.warn(`no dict data of "${dictMeta.type}" found in the response`) + return [] + } + return dicts.map(d => dictConverter(d, dictMeta)) +} + +export function mergeOptions(src) { + mergeRecursive(options, src) +} + +export default options diff --git a/ui/src/utils/dict/index.js b/ui/src/utils/dict/index.js new file mode 100644 index 0000000..215eb9e --- /dev/null +++ b/ui/src/utils/dict/index.js @@ -0,0 +1,33 @@ +import Dict from './Dict' +import { mergeOptions } from './DictOptions' + +export default function(Vue, options) { + mergeOptions(options) + Vue.mixin({ + data() { + if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) { + return {} + } + const dict = new Dict() + dict.owner = this + return { + dict + } + }, + created() { + if (!(this.dict instanceof Dict)) { + return + } + options.onCreated && options.onCreated(this.dict) + this.dict.init(this.$options.dicts).then(() => { + options.onReady && options.onReady(this.dict) + this.$nextTick(() => { + this.$emit('dictReady', this.dict) + if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) { + this.$options.methods.onDictReady.call(this, this.dict) + } + }) + }) + }, + }) +} diff --git a/ui/src/utils/errorCode.js b/ui/src/utils/errorCode.js new file mode 100644 index 0000000..d2111ee --- /dev/null +++ b/ui/src/utils/errorCode.js @@ -0,0 +1,6 @@ +export default { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + 'default': '系统未知错误,请反馈给管理员' +} diff --git a/ui/src/utils/generator/config.js b/ui/src/utils/generator/config.js new file mode 100644 index 0000000..7abf227 --- /dev/null +++ b/ui/src/utils/generator/config.js @@ -0,0 +1,438 @@ +export const formConf = { + formRef: 'elForm', + formModel: 'formData', + size: 'medium', + labelPosition: 'right', + labelWidth: 100, + formRules: 'rules', + gutter: 15, + disabled: false, + span: 24, + formBtns: true +} + +export const inputComponents = [ + { + label: '单行文本', + tag: 'el-input', + tagIcon: 'input', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '多行文本', + tag: 'el-input', + tagIcon: 'textarea', + type: 'textarea', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + autosize: { + minRows: 4, + maxRows: 4 + }, + style: { width: '100%' }, + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '密码', + tag: 'el-input', + tagIcon: 'password', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + 'show-password': true, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '计数器', + tag: 'el-input-number', + tagIcon: 'number', + placeholder: '', + defaultValue: undefined, + span: 24, + labelWidth: null, + min: undefined, + max: undefined, + step: undefined, + 'step-strictly': false, + precision: undefined, + 'controls-position': '', + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input-number' + } +] + +export const selectComponents = [ + { + label: '下拉选择', + tag: 'el-select', + tagIcon: 'select', + placeholder: '请选择', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + disabled: false, + required: true, + filterable: false, + multiple: false, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/select' + }, + { + label: '级联选择', + tag: 'el-cascader', + tagIcon: 'cascader', + placeholder: '请选择', + defaultValue: [], + span: 24, + labelWidth: null, + style: { width: '100%' }, + props: { + props: { + multiple: false + } + }, + 'show-all-levels': true, + disabled: false, + clearable: true, + filterable: false, + required: true, + options: [{ + id: 1, + value: 1, + label: '选项1', + children: [{ + id: 2, + value: 2, + label: '选项1-1' + }] + }], + dataType: 'dynamic', + labelKey: 'label', + valueKey: 'value', + childrenKey: 'children', + separator: '/', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/cascader' + }, + { + label: '单选框组', + tag: 'el-radio-group', + tagIcon: 'radio', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'medium', + disabled: false, + required: true, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/radio' + }, + { + label: '多选框组', + tag: 'el-checkbox-group', + tagIcon: 'checkbox', + defaultValue: [], + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'medium', + disabled: false, + required: true, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/checkbox' + }, + { + label: '开关', + tag: 'el-switch', + tagIcon: 'switch', + defaultValue: false, + span: 24, + labelWidth: null, + style: {}, + disabled: false, + required: true, + 'active-text': '', + 'inactive-text': '', + 'active-color': null, + 'inactive-color': null, + 'active-value': true, + 'inactive-value': false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/switch' + }, + { + label: '滑块', + tag: 'el-slider', + tagIcon: 'slider', + defaultValue: null, + span: 24, + labelWidth: null, + disabled: false, + required: true, + min: 0, + max: 100, + step: 1, + 'show-stops': false, + range: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/slider' + }, + { + label: '时间选择', + tag: 'el-time-picker', + tagIcon: 'time', + placeholder: '请选择', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'picker-options': { + selectableRange: '00:00:00-23:59:59' + }, + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' + }, + { + label: '时间范围', + tag: 'el-time-picker', + tagIcon: 'time-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'is-range': true, + 'range-separator': '至', + 'start-placeholder': '开始时间', + 'end-placeholder': '结束时间', + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' + }, + { + label: '日期选择', + tag: 'el-date-picker', + tagIcon: 'date', + placeholder: '请选择', + defaultValue: null, + type: 'date', + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + format: 'yyyy-MM-dd', + 'value-format': 'yyyy-MM-dd', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' + }, + { + label: '日期范围', + tag: 'el-date-picker', + tagIcon: 'date-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + type: 'daterange', + 'range-separator': '至', + 'start-placeholder': '开始日期', + 'end-placeholder': '结束日期', + disabled: false, + clearable: true, + required: true, + format: 'yyyy-MM-dd', + 'value-format': 'yyyy-MM-dd', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' + }, + { + label: '评分', + tag: 'el-rate', + tagIcon: 'rate', + defaultValue: 0, + span: 24, + labelWidth: null, + style: {}, + max: 5, + 'allow-half': false, + 'show-text': false, + 'show-score': false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/rate' + }, + { + label: '颜色选择', + tag: 'el-color-picker', + tagIcon: 'color', + defaultValue: null, + labelWidth: null, + 'show-alpha': false, + 'color-format': '', + disabled: false, + required: true, + size: 'medium', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/color-picker' + }, + { + label: '上传', + tag: 'el-upload', + tagIcon: 'upload', + action: 'https://jsonplaceholder.typicode.com/posts/', + defaultValue: null, + labelWidth: null, + disabled: false, + required: true, + accept: '', + name: 'file', + 'auto-upload': true, + showTip: false, + buttonText: '点击上传', + fileSize: 2, + sizeUnit: 'MB', + 'list-type': 'text', + multiple: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/upload' + } +] + +export const layoutComponents = [ + { + layout: 'rowFormItem', + tagIcon: 'row', + type: 'default', + justify: 'start', + align: 'top', + label: '行容器', + layoutTree: true, + children: [], + document: 'https://element.eleme.cn/#/zh-CN/component/layout' + }, + { + layout: 'colFormItem', + label: '按钮', + changeTag: true, + labelWidth: null, + tag: 'el-button', + tagIcon: 'button', + span: 24, + default: '主要按钮', + type: 'primary', + icon: 'el-icon-search', + size: 'medium', + disabled: false, + document: 'https://element.eleme.cn/#/zh-CN/component/button' + } +] + +// 组件rule的触发方式,无触发方式的组件不生成rule +export const trigger = { + 'el-input': 'blur', + 'el-input-number': 'blur', + 'el-select': 'change', + 'el-radio-group': 'change', + 'el-checkbox-group': 'change', + 'el-cascader': 'change', + 'el-time-picker': 'change', + 'el-date-picker': 'change', + 'el-rate': 'change' +} diff --git a/ui/src/utils/generator/css.js b/ui/src/utils/generator/css.js new file mode 100644 index 0000000..c1c62e6 --- /dev/null +++ b/ui/src/utils/generator/css.js @@ -0,0 +1,18 @@ +const styles = { + 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}', + 'el-upload': '.el-upload__tip{line-height: 1.2;}' +} + +function addCss(cssList, el) { + const css = styles[el.tag] + css && cssList.indexOf(css) === -1 && cssList.push(css) + if (el.children) { + el.children.forEach(el2 => addCss(cssList, el2)) + } +} + +export function makeUpCss(conf) { + const cssList = [] + conf.fields.forEach(el => addCss(cssList, el)) + return cssList.join('\n') +} diff --git a/ui/src/utils/generator/drawingDefault.js b/ui/src/utils/generator/drawingDefault.js new file mode 100644 index 0000000..09f133c --- /dev/null +++ b/ui/src/utils/generator/drawingDefault.js @@ -0,0 +1,29 @@ +export default [ + { + layout: 'colFormItem', + tagIcon: 'input', + label: '手机号', + vModel: 'mobile', + formId: 6, + tag: 'el-input', + placeholder: '请输入手机号', + defaultValue: '', + span: 24, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': 'el-icon-mobile', + 'suffix-icon': '', + maxlength: 11, + 'show-word-limit': true, + readonly: false, + disabled: false, + required: true, + changeTag: true, + regList: [{ + pattern: '/^1(3|4|5|7|8|9)\\d{9}$/', + message: '手机号格式错误' + }] + } +] diff --git a/ui/src/utils/generator/html.js b/ui/src/utils/generator/html.js new file mode 100644 index 0000000..9bcc536 --- /dev/null +++ b/ui/src/utils/generator/html.js @@ -0,0 +1,359 @@ +/* eslint-disable max-len */ +import { trigger } from './config' + +let confGlobal +let someSpanIsNot24 + +export function dialogWrapper(str) { + return ` + ${str} +
+ 取消 + 确定 +
+
` +} + +export function vueTemplate(str) { + return `` +} + +export function vueScript(str) { + return `` +} + +export function cssStyle(cssStr) { + return `` +} + +function buildFormTemplate(conf, child, type) { + let labelPosition = '' + if (conf.labelPosition !== 'right') { + labelPosition = `label-position="${conf.labelPosition}"` + } + const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' + let str = ` + ${child} + ${buildFromBtns(conf, type)} + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + return str +} + +function buildFromBtns(conf, type) { + let str = '' + if (conf.formBtns && type === 'file') { + str = ` + 提交 + 重置 + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + } + return str +} + +// span不为24的用el-col包裹 +function colWrapper(element, str) { + if (someSpanIsNot24 || element.span !== 24) { + return ` + ${str} + ` + } + return str +} + +const layouts = { + colFormItem(element) { + let labelWidth = '' + if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) { + labelWidth = `label-width="${element.labelWidth}px"` + } + const required = !trigger[element.tag] && element.required ? 'required' : '' + const tagDom = tags[element.tag] ? tags[element.tag](element) : null + let str = ` + ${tagDom} + ` + str = colWrapper(element, str) + return str + }, + rowFormItem(element) { + const type = element.type === 'default' ? '' : `type="${element.type}"` + const justify = element.type === 'default' ? '' : `justify="${element.justify}"` + const align = element.type === 'default' ? '' : `align="${element.align}"` + const gutter = element.gutter ? `gutter="${element.gutter}"` : '' + const children = element.children.map(el => layouts[el.layout](el)) + let str = ` + ${children.join('\n')} + ` + str = colWrapper(element, str) + return str + } +} + +const tags = { + 'el-button': el => { + const { + tag, disabled + } = attrBuilder(el) + const type = el.type ? `type="${el.type}"` : '' + const icon = el.icon ? `icon="${el.icon}"` : '' + const size = el.size ? `size="${el.size}"` : '' + let child = buildElButtonChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}` + }, + 'el-input': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' + const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' + const readonly = el.readonly ? 'readonly' : '' + const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : '' + const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : '' + const showPassword = el['show-password'] ? 'show-password' : '' + const type = el.type ? `type="${el.type}"` : '' + const autosize = el.autosize && el.autosize.minRows + ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` + : '' + let child = buildElInputChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}` + }, + 'el-input-number': el => { + const { disabled, vModel, placeholder } = attrBuilder(el) + const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' + const precision = el.precision ? `:precision='${el.precision}'` : '' + + return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>` + }, + 'el-select': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const filterable = el.filterable ? 'filterable' : '' + const multiple = el.multiple ? 'multiple' : '' + let child = buildElSelectChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}` + }, + 'el-radio-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + let child = buildElRadioGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${size} ${disabled}>${child}` + }, + 'el-checkbox-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const min = el.min ? `:min="${el.min}"` : '' + const max = el.max ? `:max="${el.max}"` : '' + let child = buildElCheckboxGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}` + }, + 'el-switch': el => { + const { disabled, vModel } = attrBuilder(el) + const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' + const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' + const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' + const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : '' + const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' + const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' + + return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>` + }, + 'el-cascader': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const options = el.options ? `:options="${el.vModel}Options"` : '' + const props = el.props ? `:props="${el.vModel}Props"` : '' + const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' + const filterable = el.filterable ? 'filterable' : '' + const separator = el.separator === '/' ? '' : `separator="${el.separator}"` + + return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>` + }, + 'el-slider': el => { + const { disabled, vModel } = attrBuilder(el) + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const range = el.range ? 'range' : '' + const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' + + return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>` + }, + 'el-time-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const isRange = el['is-range'] ? 'is-range' : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' + + return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>` + }, + 'el-date-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const type = el.type === 'date' ? '' : `type="${el.type}"` + const readonly = el.readonly ? 'readonly' : '' + + return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>` + }, + 'el-rate': el => { + const { disabled, vModel } = attrBuilder(el) + const max = el.max ? `:max='${el.max}'` : '' + const allowHalf = el['allow-half'] ? 'allow-half' : '' + const showText = el['show-text'] ? 'show-text' : '' + const showScore = el['show-score'] ? 'show-score' : '' + + return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}>` + }, + 'el-color-picker': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const showAlpha = el['show-alpha'] ? 'show-alpha' : '' + const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' + + return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>` + }, + 'el-upload': el => { + const disabled = el.disabled ? ':disabled=\'true\'' : '' + const action = el.action ? `:action="${el.vModel}Action"` : '' + const multiple = el.multiple ? 'multiple' : '' + const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' + const accept = el.accept ? `accept="${el.accept}"` : '' + const name = el.name !== 'file' ? `name="${el.name}"` : '' + const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' + const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"` + const fileList = `:file-list="${el.vModel}fileList"` + const ref = `ref="${el.vModel}"` + let child = buildElUploadChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}` + } +} + +function attrBuilder(el) { + return { + vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`, + clearable: el.clearable ? 'clearable' : '', + placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', + width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', + disabled: el.disabled ? ':disabled=\'true\'' : '' + } +} + +// el-buttin 子级 +function buildElButtonChild(conf) { + const children = [] + if (conf.default) { + children.push(conf.default) + } + return children.join('\n') +} + +// el-input innerHTML +function buildElInputChild(conf) { + const children = [] + if (conf.prepend) { + children.push(``) + } + if (conf.append) { + children.push(``) + } + return children.join('\n') +} + +function buildElSelectChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + children.push(``) + } + return children.join('\n') +} + +function buildElRadioGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElCheckboxGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElUploadChild(conf) { + const list = [] + if (conf['list-type'] === 'picture-card') list.push('') + else list.push(`${conf.buttonText}`) + if (conf.showTip) list.push(`
只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件
`) + return list.join('\n') +} + +export function makeUpHtml(conf, type) { + const htmlList = [] + confGlobal = conf + someSpanIsNot24 = conf.fields.some(item => item.span !== 24) + conf.fields.forEach(el => { + htmlList.push(layouts[el.layout](el)) + }) + const htmlStr = htmlList.join('\n') + + let temp = buildFormTemplate(conf, htmlStr, type) + if (type === 'dialog') { + temp = dialogWrapper(temp) + } + confGlobal = null + return temp +} diff --git a/ui/src/utils/generator/icon.json b/ui/src/utils/generator/icon.json new file mode 100644 index 0000000..2d9999a --- /dev/null +++ b/ui/src/utils/generator/icon.json @@ -0,0 +1 @@ +["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"] \ No newline at end of file diff --git a/ui/src/utils/generator/js.js b/ui/src/utils/generator/js.js new file mode 100644 index 0000000..35e3e21 --- /dev/null +++ b/ui/src/utils/generator/js.js @@ -0,0 +1,236 @@ +import { isArray } from 'util' +import { exportDefault, titleCase } from '@/utils/index' +import { trigger } from './config' + +const units = { + KB: '1024', + MB: '1024 / 1024', + GB: '1024 / 1024 / 1024' +} +let confGlobal +const inheritAttrs = { + file: '', + dialog: 'inheritAttrs: false,' +} + + +export function makeUpJs(conf, type) { + confGlobal = conf = JSON.parse(JSON.stringify(conf)) + const dataList = [] + const ruleList = [] + const optionsList = [] + const propsList = [] + const methodList = mixinMethod(type) + const uploadVarList = [] + + conf.fields.forEach(el => { + buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) + }) + + const script = buildexport( + conf, + type, + dataList.join('\n'), + ruleList.join('\n'), + optionsList.join('\n'), + uploadVarList.join('\n'), + propsList.join('\n'), + methodList.join('\n') + ) + confGlobal = null + return script +} + +function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) { + buildData(el, dataList) + buildRules(el, ruleList) + + if (el.options && el.options.length) { + buildOptions(el, optionsList) + if (el.dataType === 'dynamic') { + const model = `${el.vModel}Options` + const options = titleCase(model) + buildOptionMethod(`get${options}`, model, methodList) + } + } + + if (el.props && el.props.props) { + buildProps(el, propsList) + } + + if (el.action && el.tag === 'el-upload') { + uploadVarList.push( + `${el.vModel}Action: '${el.action}', + ${el.vModel}fileList: [],` + ) + methodList.push(buildBeforeUpload(el)) + if (!el['auto-upload']) { + methodList.push(buildSubmitUpload(el)) + } + } + + if (el.children) { + el.children.forEach(el2 => { + buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) + }) + } +} + +function mixinMethod(type) { + const list = []; const + minxins = { + file: confGlobal.formBtns ? { + submitForm: `submitForm() { + this.$refs['${confGlobal.formRef}'].validate(valid => { + if(!valid) return + // TODO 提交表单 + }) + },`, + resetForm: `resetForm() { + this.$refs['${confGlobal.formRef}'].resetFields() + },` + } : null, + dialog: { + onOpen: 'onOpen() {},', + onClose: `onClose() { + this.$refs['${confGlobal.formRef}'].resetFields() + },`, + close: `close() { + this.$emit('update:visible', false) + },`, + handleConfirm: `handleConfirm() { + this.$refs['${confGlobal.formRef}'].validate(valid => { + if(!valid) return + this.close() + }) + },` + } + } + + const methods = minxins[type] + if (methods) { + Object.keys(methods).forEach(key => { + list.push(methods[key]) + }) + } + + return list +} + +function buildData(conf, dataList) { + if (conf.vModel === undefined) return + let defaultValue + if (typeof (conf.defaultValue) === 'string' && !conf.multiple) { + defaultValue = `'${conf.defaultValue}'` + } else { + defaultValue = `${JSON.stringify(conf.defaultValue)}` + } + dataList.push(`${conf.vModel}: ${defaultValue},`) +} + +function buildRules(conf, ruleList) { + if (conf.vModel === undefined) return + const rules = [] + if (trigger[conf.tag]) { + if (conf.required) { + const type = isArray(conf.defaultValue) ? 'type: \'array\',' : '' + let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder + if (message === undefined) message = `${conf.label}不能为空` + rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`) + } + if (conf.regList && isArray(conf.regList)) { + conf.regList.forEach(item => { + if (item.pattern) { + rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`) + } + }) + } + ruleList.push(`${conf.vModel}: [${rules.join(',')}],`) + } +} + +function buildOptions(conf, optionsList) { + if (conf.vModel === undefined) return + if (conf.dataType === 'dynamic') { conf.options = [] } + const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},` + optionsList.push(str) +} + +function buildProps(conf, propsList) { + if (conf.dataType === 'dynamic') { + conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey) + conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey) + conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey) + } + const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},` + propsList.push(str) +} + +function buildBeforeUpload(conf) { + const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const + returnList = [] + if (conf.fileSize) { + rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize} + if(!isRightSize){ + this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}') + }` + returnList.push('isRightSize') + } + if (conf.accept) { + acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type) + if(!isAccept){ + this.$message.error('应该选择${conf.accept}类型的文件') + }` + returnList.push('isAccept') + } + const str = `${conf.vModel}BeforeUpload(file) { + ${rightSizeCode} + ${acceptCode} + return ${returnList.join('&&')} + },` + return returnList.length ? str : '' +} + +function buildSubmitUpload(conf) { + const str = `submitUpload() { + this.$refs['${conf.vModel}'].submit() + },` + return str +} + +function buildOptionMethod(methodName, model, methodList) { + const str = `${methodName}() { + // TODO 发起请求获取数据 + this.${model} + },` + methodList.push(str) +} + +function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) { + const str = `${exportDefault}{ + ${inheritAttrs[type]} + components: {}, + props: [], + data () { + return { + ${conf.formModel}: { + ${data} + }, + ${conf.formRules}: { + ${rules} + }, + ${uploadVar} + ${selectOptions} + ${props} + } + }, + computed: {}, + watch: {}, + created () {}, + mounted () {}, + methods: { + ${methods} + } +}` + return str +} diff --git a/ui/src/utils/generator/render.js b/ui/src/utils/generator/render.js new file mode 100644 index 0000000..e8640f0 --- /dev/null +++ b/ui/src/utils/generator/render.js @@ -0,0 +1,126 @@ +import { makeMap } from '@/utils/index' + +// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js +const isAttr = makeMap( + 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' + + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' + + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,' + + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,' + + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' + + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' + + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' + + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' + + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' + + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' + + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' + + 'target,title,type,usemap,value,width,wrap' +) + +function vModel(self, dataObject, defaultValue) { + dataObject.props.value = defaultValue + + dataObject.on.input = val => { + self.$emit('input', val) + } +} + +const componentChild = { + 'el-button': { + default(h, conf, key) { + return conf[key] + }, + }, + 'el-input': { + prepend(h, conf, key) { + return + }, + append(h, conf, key) { + return + } + }, + 'el-select': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + list.push() + }) + return list + } + }, + 'el-radio-group': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + if (conf.optionType === 'button') list.push({item.label}) + else list.push({item.label}) + }) + return list + } + }, + 'el-checkbox-group': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + if (conf.optionType === 'button') { + list.push({item.label}) + } else { + list.push({item.label}) + } + }) + return list + } + }, + 'el-upload': { + 'list-type': (h, conf, key) => { + const list = [] + if (conf['list-type'] === 'picture-card') { + list.push() + } else { + list.push({conf.buttonText}) + } + if (conf.showTip) { + list.push(
只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件
) + } + return list + } + } +} + +export default { + render(h) { + const dataObject = { + attrs: {}, + props: {}, + on: {}, + style: {} + } + const confClone = JSON.parse(JSON.stringify(this.conf)) + const children = [] + + const childObjs = componentChild[confClone.tag] + if (childObjs) { + Object.keys(childObjs).forEach(key => { + const childFunc = childObjs[key] + if (confClone[key]) { + children.push(childFunc(h, confClone, key)) + } + }) + } + + Object.keys(confClone).forEach(key => { + const val = confClone[key] + if (key === 'vModel') { + vModel(this, dataObject, confClone.defaultValue) + } else if (dataObject[key]) { + dataObject[key] = val + } else if (!isAttr(key)) { + dataObject.props[key] = val + } else { + dataObject.attrs[key] = val + } + }) + return h(this.conf.tag, dataObject, children) + }, + props: ['conf'] +} diff --git a/ui/src/utils/index.js b/ui/src/utils/index.js new file mode 100644 index 0000000..df5db12 --- /dev/null +++ b/ui/src/utils/index.js @@ -0,0 +1,390 @@ +import { parseTime } from './ruoyi' + +/** + * 表格时间格式化 + */ +export function formatDate(cellValue) { + if (cellValue == null || cellValue == "") return ""; + var date = new Date(cellValue) + var year = date.getFullYear() + var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param {string} input value + * @returns {number} output value + */ +export function byteLength(str) { + // returns the byte length of an utf8 string + let s = str.length + for (var i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param {Array} actual + * @returns {Array} + */ +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param {Object} json + * @returns {Array} + */ +export function param(json) { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') + if (!search) { + return {} + } + const obj = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj +} + +/** + * @param {string} val + * @returns {string} + */ +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export function objectMerge(target, source) { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param {string} type + * @returns {Date} + */ +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +/** + * @param {Array} arr + * @returns {Array} + */ +export function uniqueArr(arr) { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString() { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele, cls) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele, cls) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele, cls) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} + +export function makeMap(str, expectsLowerCase) { + const map = Object.create(null) + const list = str.split(',') + for (let i = 0; i < list.length; i++) { + map[list[i]] = true + } + return expectsLowerCase + ? val => map[val.toLowerCase()] + : val => map[val] +} + +export const exportDefault = 'export default ' + +export const beautifierConf = { + html: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'separate', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: false, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + }, + js: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'normal', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: true, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + } +} + +// 首字母大小 +export function titleCase(str) { + return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) +} + +// 下划转驼峰 +export function camelCase(str) { + return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) +} + +export function isNumberStr(str) { + return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) +} + diff --git a/ui/src/utils/jsencrypt.js b/ui/src/utils/jsencrypt.js new file mode 100644 index 0000000..78d9523 --- /dev/null +++ b/ui/src/utils/jsencrypt.js @@ -0,0 +1,30 @@ +import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' + +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' + +const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + + '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + + 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + + 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + + 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + + 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + + 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + + 'UP8iWi1Qw0Y=' + +// 加密 +export function encrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 设置公钥 + return encryptor.encrypt(txt) // 对数据进行加密 +} + +// 解密 +export function decrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPrivateKey(privateKey) // 设置私钥 + return encryptor.decrypt(txt) // 对数据进行解密 +} + diff --git a/ui/src/utils/permission.js b/ui/src/utils/permission.js new file mode 100644 index 0000000..bd4c066 --- /dev/null +++ b/ui/src/utils/permission.js @@ -0,0 +1,51 @@ +import store from '@/store' + +/** + * 字符权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkPermi(value) { + if (value && value instanceof Array && value.length > 0) { + const permissions = store.getters && store.getters.permissions + const permissionDatas = value + const all_permission = "*:*:*"; + + const hasPermission = permissions.some(permission => { + return all_permission === permission || permissionDatas.includes(permission) + }) + + if (!hasPermission) { + return false + } + return true + } else { + console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`) + return false + } +} + +/** + * 角色权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkRole(value) { + if (value && value instanceof Array && value.length > 0) { + const roles = store.getters && store.getters.roles + const permissionRoles = value + const super_admin = "admin"; + + const hasRole = roles.some(role => { + return super_admin === role || permissionRoles.includes(role) + }) + + if (!hasRole) { + return false + } + return true + } else { + console.error(`need roles! Like checkRole="['admin','editor']"`) + return false + } +} diff --git a/ui/src/utils/request.js b/ui/src/utils/request.js new file mode 100644 index 0000000..f94fa0e --- /dev/null +++ b/ui/src/utils/request.js @@ -0,0 +1,148 @@ +import axios from 'axios' +import { Notification, MessageBox, Message, Loading } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { tansParams, blobValidate } from "@/utils/ruoyi"; +import cache from '@/plugins/cache' +import { saveAs } from 'file-saver' + +let downloadLoadingInstance; +// 是否显示重新登录 +export let isRelogin = { show: false }; + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' +// 对应国际化资源文件后缀 +axios.defaults.headers['Content-Language'] = 'zh_CN' +// 创建axios实例 +const service = axios.create({ + // axios中请求配置有baseURL选项,表示请求URL公共部分 + baseURL: process.env.VUE_APP_BASE_API, + // 超时 + timeout: 10000 +}) + +// request拦截器 +service.interceptors.request.use(config => { + // 是否需要设置 token + const isToken = (config.headers || {}).isToken === false + // 是否需要防止数据重复提交 + const isRepeatSubmit = (config.headers || {}).repeatSubmit === false + if (getToken() && !isToken) { + config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + } + // get请求映射params参数 + if (config.method === 'get' && config.params) { + let url = config.url + '?' + tansParams(config.params); + url = url.slice(0, -1); + config.params = {}; + config.url = url; + } + if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { + const requestObj = { + url: config.url, + data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + time: new Date().getTime() + } + const sessionObj = cache.session.getJSON('sessionObj') + if (sessionObj === undefined || sessionObj === null || sessionObj === '') { + cache.session.setJSON('sessionObj', requestObj) + } else { + const s_url = sessionObj.url; // 请求地址 + const s_data = sessionObj.data; // 请求数据 + const s_time = sessionObj.time; // 请求时间 + const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交 + if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { + const message = '数据正在处理,请勿重复提交'; + console.warn(`[${s_url}]: ` + message) + return Promise.reject(new Error(message)) + } else { + cache.session.setJSON('sessionObj', requestObj) + } + } + } + return config +}, error => { + console.log(error) + Promise.reject(error) +}) + +// 响应拦截器 +service.interceptors.response.use(res => { + // 未设置状态码则默认成功状态 + const code = res.data.code || 200; + // 获取错误信息 + const msg = errorCode[code] || res.data.msg || errorCode['default'] + // 二进制数据则直接返回 + if(res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer'){ + return res.data + } + if (code === 401) { + if (!isRelogin.show) { + isRelogin.show = true; + MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { + isRelogin.show = false; + store.dispatch('LogOut').then(() => { + location.href = process.env.VUE_APP_CONTEXT_PATH + "index"; + }) + }).catch(() => { + isRelogin.show = false; + }); + } + return Promise.reject('无效的会话,或者会话已过期,请重新登录。') + } else if (code === 500) { + Message({ message: msg, type: 'error' }) + return Promise.reject(new Error(msg)) + } else if (code === 601) { + Message({ message: msg, type: 'warning' }) + return Promise.reject('error') + } else if (code !== 200) { + Notification.error({ title: msg }) + return Promise.reject('error') + } else { + return res.data + } + }, + error => { + console.log('err' + error) + let { message } = error; + if (message == "Network Error") { + message = "后端接口连接异常"; + } else if (message.includes("timeout")) { + message = "系统接口请求超时"; + } else if (message.includes("Request failed with status code")) { + message = "系统接口" + message.substr(message.length - 3) + "异常"; + } + Message({ message: message, type: 'error', duration: 5 * 1000 }) + return Promise.reject(error) + } +) + +// 通用下载方法 +export function download(url, params, filename, config) { + downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) + return service.post(url, params, { + transformRequest: [(params) => { return tansParams(params) }], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + responseType: 'blob', + ...config + }).then(async (data) => { + const isLogin = await blobValidate(data); + if (isLogin) { + const blob = new Blob([data]) + saveAs(blob, filename) + } else { + const resText = await data.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + Message.error(errMsg); + } + downloadLoadingInstance.close(); + }).catch((r) => { + console.error(r) + Message.error('下载文件出现错误,请联系管理员!') + downloadLoadingInstance.close(); + }) +} + +export default service diff --git a/ui/src/utils/ruoyi.js b/ui/src/utils/ruoyi.js new file mode 100644 index 0000000..243c4c7 --- /dev/null +++ b/ui/src/utils/ruoyi.js @@ -0,0 +1,239 @@ + + +/** + * 通用js方法封装处理 + * Copyright (c) 2019 ruoyi + */ + +// 日期格式化 +export function parseTime(time, pattern) { + if (arguments.length === 0 || !time) { + return null + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } else if (typeof time === 'string') { + time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), ''); + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +// 表单重置 +export function resetForm(refName) { + if (this.$refs[refName]) { + this.$refs[refName].resetFields(); + } +} + +// 添加日期范围 +export function addDateRange(params, dateRange, propName) { + let search = params; + search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}; + dateRange = Array.isArray(dateRange) ? dateRange : []; + if (typeof (propName) === 'undefined') { + search.params['beginTime'] = dateRange[0]; + search.params['endTime'] = dateRange[1]; + } else { + search.params['begin' + propName] = dateRange[0]; + search.params['end' + propName] = dateRange[1]; + } + return search; +} + +// 回显数据字典 +export function selectDictLabel(datas, value) { + if (value === undefined) { + return ""; + } + var actions = []; + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + value)) { + actions.push(datas[key].label); + return true; + } + }) + if (actions.length === 0) { + actions.push(value); + } + return actions.join(''); +} + +// 回显数据字典(字符串、数组) +export function selectDictLabels(datas, value, separator) { + if (value === undefined || value.length ===0) { + return ""; + } + if (Array.isArray(value)) { + value = value.join(","); + } + var actions = []; + var currentSeparator = undefined === separator ? "," : separator; + var temp = value.split(currentSeparator); + Object.keys(value.split(currentSeparator)).some((val) => { + var match = false; + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + temp[val])) { + actions.push(datas[key].label + currentSeparator); + match = true; + } + }) + if (!match) { + actions.push(temp[val] + currentSeparator); + } + }) + return actions.join('').substring(0, actions.join('').length - 1); +} + +// 字符串格式化(%s ) +export function sprintf(str) { + var args = arguments, flag = true, i = 1; + str = str.replace(/%s/g, function () { + var arg = args[i++]; + if (typeof arg === 'undefined') { + flag = false; + return ''; + } + return arg; + }); + return flag ? str : ''; +} + +// 转换字符串,undefined,null等转化为"" +export function parseStrEmpty(str) { + if (!str || str == "undefined" || str == "null") { + return ""; + } + return str; +} + +// 数据合并 +export function mergeRecursive(source, target) { + for (var p in target) { + try { + if (target[p].constructor == Object) { + source[p] = mergeRecursive(source[p], target[p]); + } else { + source[p] = target[p]; + } + } catch (e) { + source[p] = target[p]; + } + } + return source; +}; + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export function handleTree(data, id, parentId, children) { + let config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + }; + + var childrenListMap = {}; + var nodeIds = {}; + var tree = []; + + for (let d of data) { + let parentId = d[config.parentId]; + if (childrenListMap[parentId] == null) { + childrenListMap[parentId] = []; + } + nodeIds[d[config.id]] = d; + childrenListMap[parentId].push(d); + } + + for (let d of data) { + let parentId = d[config.parentId]; + if (nodeIds[parentId] == null) { + tree.push(d); + } + } + + for (let t of tree) { + adaptToChildrenList(t); + } + + function adaptToChildrenList(o) { + if (childrenListMap[o[config.id]] !== null) { + o[config.childrenList] = childrenListMap[o[config.id]]; + } + if (o[config.childrenList]) { + for (let c of o[config.childrenList]) { + adaptToChildrenList(c); + } + } + } + return tree; +} + +/** +* 参数处理 +* @param {*} params 参数 +*/ +export function tansParams(params) { + let result = '' + for (const propName of Object.keys(params)) { + const value = params[propName]; + var part = encodeURIComponent(propName) + "="; + if (value !== null && value !== "" && typeof (value) !== "undefined") { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') { + let params = propName + '[' + key + ']'; + var subPart = encodeURIComponent(params) + "="; + result += subPart + encodeURIComponent(value[key]) + "&"; + } + } + } else { + result += part + encodeURIComponent(value) + "&"; + } + } + } + return result +} + +// 验证是否为blob格式 +export async function blobValidate(data) { + try { + const text = await data.text(); + JSON.parse(text); + return false; + } catch (error) { + return true; + } +} diff --git a/ui/src/utils/scroll-to.js b/ui/src/utils/scroll-to.js new file mode 100644 index 0000000..c5d8e04 --- /dev/null +++ b/ui/src/utils/scroll-to.js @@ -0,0 +1,58 @@ +Math.easeInOutQuad = function(t, b, c, d) { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +var requestAnimFrame = (function() { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +function move(amount) { + document.documentElement.scrollTop = amount + document.body.parentNode.scrollTop = amount + document.body.scrollTop = amount +} + +function position() { + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop +} + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export function scrollTo(to, duration, callback) { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + var animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + var val = Math.easeInOutQuad(currentTime, start, change, duration) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +} diff --git a/ui/src/utils/validate.js b/ui/src/utils/validate.js new file mode 100644 index 0000000..adfa254 --- /dev/null +++ b/ui/src/utils/validate.js @@ -0,0 +1,83 @@ +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +/** + * @param {string} url + * @returns {Boolean} + */ +export function validURL(url) { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @param {string} email + * @returns {Boolean} + */ +export function validEmail(email) { + const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return reg.test(email) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function isString(str) { + if (typeof str === 'string' || str instanceof String) { + return true + } + return false +} + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export function isArray(arg) { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} diff --git a/ui/src/views/components/icons/element-icons.js b/ui/src/views/components/icons/element-icons.js new file mode 100644 index 0000000..9ea4d63 --- /dev/null +++ b/ui/src/views/components/icons/element-icons.js @@ -0,0 +1,3 @@ +const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round'] + +export default elementIcons diff --git a/ui/src/views/components/icons/index.vue b/ui/src/views/components/icons/index.vue new file mode 100644 index 0000000..d3c9a71 --- /dev/null +++ b/ui/src/views/components/icons/index.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/ui/src/views/components/icons/svg-icons.js b/ui/src/views/components/icons/svg-icons.js new file mode 100644 index 0000000..724cd8e --- /dev/null +++ b/ui/src/views/components/icons/svg-icons.js @@ -0,0 +1,10 @@ +const req = require.context('../../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const svgIcons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default svgIcons diff --git a/ui/src/views/dashboard/BarChart.vue b/ui/src/views/dashboard/BarChart.vue new file mode 100644 index 0000000..88e7ef6 --- /dev/null +++ b/ui/src/views/dashboard/BarChart.vue @@ -0,0 +1,102 @@ + + + diff --git a/ui/src/views/dashboard/LineChart.vue b/ui/src/views/dashboard/LineChart.vue new file mode 100644 index 0000000..702ff73 --- /dev/null +++ b/ui/src/views/dashboard/LineChart.vue @@ -0,0 +1,135 @@ + + + diff --git a/ui/src/views/dashboard/PanelGroup.vue b/ui/src/views/dashboard/PanelGroup.vue new file mode 100644 index 0000000..1a1081f --- /dev/null +++ b/ui/src/views/dashboard/PanelGroup.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/ui/src/views/dashboard/PieChart.vue b/ui/src/views/dashboard/PieChart.vue new file mode 100644 index 0000000..63f0d84 --- /dev/null +++ b/ui/src/views/dashboard/PieChart.vue @@ -0,0 +1,79 @@ + + + diff --git a/ui/src/views/dashboard/RaddarChart.vue b/ui/src/views/dashboard/RaddarChart.vue new file mode 100644 index 0000000..312e018 --- /dev/null +++ b/ui/src/views/dashboard/RaddarChart.vue @@ -0,0 +1,116 @@ + + + diff --git a/ui/src/views/dashboard/mixins/resize.js b/ui/src/views/dashboard/mixins/resize.js new file mode 100644 index 0000000..b1e76e9 --- /dev/null +++ b/ui/src/views/dashboard/mixins/resize.js @@ -0,0 +1,56 @@ +import { debounce } from '@/utils' + +export default { + data() { + return { + $_sidebarElm: null, + $_resizeHandler: null + } + }, + mounted() { + this.initListener() + }, + activated() { + if (!this.$_resizeHandler) { + // avoid duplication init + this.initListener() + } + + // when keep-alive chart activated, auto resize + this.resize() + }, + beforeDestroy() { + this.destroyListener() + }, + deactivated() { + this.destroyListener() + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_sidebarResizeHandler(e) { + if (e.propertyName === 'width') { + this.$_resizeHandler() + } + }, + initListener() { + this.$_resizeHandler = debounce(() => { + this.resize() + }, 100) + window.addEventListener('resize', this.$_resizeHandler) + + this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] + this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) + }, + destroyListener() { + window.removeEventListener('resize', this.$_resizeHandler) + this.$_resizeHandler = null + + this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) + }, + resize() { + const { chart } = this + chart && chart.resize() + } + } +} diff --git a/ui/src/views/demo/demo/index.vue b/ui/src/views/demo/demo/index.vue new file mode 100644 index 0000000..65387c9 --- /dev/null +++ b/ui/src/views/demo/demo/index.vue @@ -0,0 +1,431 @@ + + + diff --git a/ui/src/views/demo/tree/index.vue b/ui/src/views/demo/tree/index.vue new file mode 100644 index 0000000..6cfa7f2 --- /dev/null +++ b/ui/src/views/demo/tree/index.vue @@ -0,0 +1,313 @@ + + + diff --git a/ui/src/views/error/401.vue b/ui/src/views/error/401.vue new file mode 100644 index 0000000..448b6ec --- /dev/null +++ b/ui/src/views/error/401.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/ui/src/views/error/404.vue b/ui/src/views/error/404.vue new file mode 100644 index 0000000..96f075c --- /dev/null +++ b/ui/src/views/error/404.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/ui/src/views/index.vue b/ui/src/views/index.vue new file mode 100644 index 0000000..7f7008e --- /dev/null +++ b/ui/src/views/index.vue @@ -0,0 +1,191 @@ + + + + + + diff --git a/ui/src/views/index_v1.vue b/ui/src/views/index_v1.vue new file mode 100644 index 0000000..d2d2ec6 --- /dev/null +++ b/ui/src/views/index_v1.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/ui/src/views/login.vue b/ui/src/views/login.vue new file mode 100644 index 0000000..ed8d971 --- /dev/null +++ b/ui/src/views/login.vue @@ -0,0 +1,219 @@ + + + + + diff --git a/ui/src/views/monitor/admin/index.vue b/ui/src/views/monitor/admin/index.vue new file mode 100644 index 0000000..44322a6 --- /dev/null +++ b/ui/src/views/monitor/admin/index.vue @@ -0,0 +1,15 @@ + + diff --git a/ui/src/views/monitor/cache/index.vue b/ui/src/views/monitor/cache/index.vue new file mode 100644 index 0000000..53f6eee --- /dev/null +++ b/ui/src/views/monitor/cache/index.vue @@ -0,0 +1,146 @@ + + + diff --git a/ui/src/views/monitor/cache/list.vue b/ui/src/views/monitor/cache/list.vue new file mode 100644 index 0000000..94307ab --- /dev/null +++ b/ui/src/views/monitor/cache/list.vue @@ -0,0 +1,241 @@ + + + diff --git a/ui/src/views/monitor/logininfor/index.vue b/ui/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..8a9b17a --- /dev/null +++ b/ui/src/views/monitor/logininfor/index.vue @@ -0,0 +1,245 @@ + + + + diff --git a/ui/src/views/monitor/online/index.vue b/ui/src/views/monitor/online/index.vue new file mode 100644 index 0000000..ad613c9 --- /dev/null +++ b/ui/src/views/monitor/online/index.vue @@ -0,0 +1,122 @@ + + + + diff --git a/ui/src/views/monitor/operlog/index.vue b/ui/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..c1b5377 --- /dev/null +++ b/ui/src/views/monitor/operlog/index.vue @@ -0,0 +1,305 @@ + + + + diff --git a/ui/src/views/monitor/xxljob/index.vue b/ui/src/views/monitor/xxljob/index.vue new file mode 100644 index 0000000..7167b48 --- /dev/null +++ b/ui/src/views/monitor/xxljob/index.vue @@ -0,0 +1,15 @@ + + diff --git a/ui/src/views/redirect.vue b/ui/src/views/redirect.vue new file mode 100644 index 0000000..db4c1d6 --- /dev/null +++ b/ui/src/views/redirect.vue @@ -0,0 +1,12 @@ + diff --git a/ui/src/views/register.vue b/ui/src/views/register.vue new file mode 100644 index 0000000..649c4d8 --- /dev/null +++ b/ui/src/views/register.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/ui/src/views/system/config/index.vue b/ui/src/views/system/config/index.vue new file mode 100644 index 0000000..f580b98 --- /dev/null +++ b/ui/src/views/system/config/index.vue @@ -0,0 +1,343 @@ + + + diff --git a/ui/src/views/system/dept/index.vue b/ui/src/views/system/dept/index.vue new file mode 100644 index 0000000..e502b4e --- /dev/null +++ b/ui/src/views/system/dept/index.vue @@ -0,0 +1,340 @@ + + + diff --git a/ui/src/views/system/dict/data.vue b/ui/src/views/system/dict/data.vue new file mode 100644 index 0000000..c36cffc --- /dev/null +++ b/ui/src/views/system/dict/data.vue @@ -0,0 +1,402 @@ + + + diff --git a/ui/src/views/system/dict/index.vue b/ui/src/views/system/dict/index.vue new file mode 100644 index 0000000..b6ed788 --- /dev/null +++ b/ui/src/views/system/dict/index.vue @@ -0,0 +1,347 @@ + + + diff --git a/ui/src/views/system/menu/index.vue b/ui/src/views/system/menu/index.vue new file mode 100644 index 0000000..52eb8b9 --- /dev/null +++ b/ui/src/views/system/menu/index.vue @@ -0,0 +1,453 @@ + + + diff --git a/ui/src/views/system/notice/index.vue b/ui/src/views/system/notice/index.vue new file mode 100644 index 0000000..19e25a0 --- /dev/null +++ b/ui/src/views/system/notice/index.vue @@ -0,0 +1,312 @@ + + + \ No newline at end of file diff --git a/ui/src/views/system/oss/config.vue b/ui/src/views/system/oss/config.vue new file mode 100644 index 0000000..0120641 --- /dev/null +++ b/ui/src/views/system/oss/config.vue @@ -0,0 +1,406 @@ + + + diff --git a/ui/src/views/system/oss/index.vue b/ui/src/views/system/oss/index.vue new file mode 100644 index 0000000..9f8557c --- /dev/null +++ b/ui/src/views/system/oss/index.vue @@ -0,0 +1,414 @@ + + + diff --git a/ui/src/views/system/post/index.vue b/ui/src/views/system/post/index.vue new file mode 100644 index 0000000..444bf63 --- /dev/null +++ b/ui/src/views/system/post/index.vue @@ -0,0 +1,309 @@ + + + diff --git a/ui/src/views/system/role/authUser.vue b/ui/src/views/system/role/authUser.vue new file mode 100644 index 0000000..147aa33 --- /dev/null +++ b/ui/src/views/system/role/authUser.vue @@ -0,0 +1,199 @@ + + + \ No newline at end of file diff --git a/ui/src/views/system/role/index.vue b/ui/src/views/system/role/index.vue new file mode 100644 index 0000000..ca02f3a --- /dev/null +++ b/ui/src/views/system/role/index.vue @@ -0,0 +1,606 @@ + + + + diff --git a/ui/src/views/system/role/selectUser.vue b/ui/src/views/system/role/selectUser.vue new file mode 100644 index 0000000..b2b072f --- /dev/null +++ b/ui/src/views/system/role/selectUser.vue @@ -0,0 +1,138 @@ + + + diff --git a/ui/src/views/system/user/authRole.vue b/ui/src/views/system/user/authRole.vue new file mode 100644 index 0000000..7abe26a --- /dev/null +++ b/ui/src/views/system/user/authRole.vue @@ -0,0 +1,117 @@ + + + diff --git a/ui/src/views/system/user/index.vue b/ui/src/views/system/user/index.vue new file mode 100644 index 0000000..b2120e6 --- /dev/null +++ b/ui/src/views/system/user/index.vue @@ -0,0 +1,670 @@ + + + diff --git a/ui/src/views/system/user/profile/index.vue b/ui/src/views/system/user/profile/index.vue new file mode 100644 index 0000000..2cc7c2f --- /dev/null +++ b/ui/src/views/system/user/profile/index.vue @@ -0,0 +1,91 @@ + + + diff --git a/ui/src/views/system/user/profile/resetPwd.vue b/ui/src/views/system/user/profile/resetPwd.vue new file mode 100644 index 0000000..64e8f8c --- /dev/null +++ b/ui/src/views/system/user/profile/resetPwd.vue @@ -0,0 +1,68 @@ + + + diff --git a/ui/src/views/system/user/profile/userAvatar.vue b/ui/src/views/system/user/profile/userAvatar.vue new file mode 100644 index 0000000..18b23a4 --- /dev/null +++ b/ui/src/views/system/user/profile/userAvatar.vue @@ -0,0 +1,190 @@ + + + + diff --git a/ui/src/views/system/user/profile/userInfo.vue b/ui/src/views/system/user/profile/userInfo.vue new file mode 100644 index 0000000..068b714 --- /dev/null +++ b/ui/src/views/system/user/profile/userInfo.vue @@ -0,0 +1,75 @@ + + + diff --git a/ui/src/views/tool/build/CodeTypeDialog.vue b/ui/src/views/tool/build/CodeTypeDialog.vue new file mode 100644 index 0000000..b5c2e2e --- /dev/null +++ b/ui/src/views/tool/build/CodeTypeDialog.vue @@ -0,0 +1,106 @@ + + diff --git a/ui/src/views/tool/build/DraggableItem.vue b/ui/src/views/tool/build/DraggableItem.vue new file mode 100644 index 0000000..e881778 --- /dev/null +++ b/ui/src/views/tool/build/DraggableItem.vue @@ -0,0 +1,100 @@ + diff --git a/ui/src/views/tool/build/IconsDialog.vue b/ui/src/views/tool/build/IconsDialog.vue new file mode 100644 index 0000000..958be50 --- /dev/null +++ b/ui/src/views/tool/build/IconsDialog.vue @@ -0,0 +1,123 @@ + + + diff --git a/ui/src/views/tool/build/RightPanel.vue b/ui/src/views/tool/build/RightPanel.vue new file mode 100644 index 0000000..c2760eb --- /dev/null +++ b/ui/src/views/tool/build/RightPanel.vue @@ -0,0 +1,946 @@ + + + + + diff --git a/ui/src/views/tool/build/TreeNodeDialog.vue b/ui/src/views/tool/build/TreeNodeDialog.vue new file mode 100644 index 0000000..fa7f0b2 --- /dev/null +++ b/ui/src/views/tool/build/TreeNodeDialog.vue @@ -0,0 +1,149 @@ + + diff --git a/ui/src/views/tool/build/index.vue b/ui/src/views/tool/build/index.vue new file mode 100644 index 0000000..d679422 --- /dev/null +++ b/ui/src/views/tool/build/index.vue @@ -0,0 +1,783 @@ + + + + + diff --git a/ui/src/views/tool/gen/basicInfoForm.vue b/ui/src/views/tool/gen/basicInfoForm.vue new file mode 100644 index 0000000..7029529 --- /dev/null +++ b/ui/src/views/tool/gen/basicInfoForm.vue @@ -0,0 +1,60 @@ + + + diff --git a/ui/src/views/tool/gen/editTable.vue b/ui/src/views/tool/gen/editTable.vue new file mode 100644 index 0000000..f34327d --- /dev/null +++ b/ui/src/views/tool/gen/editTable.vue @@ -0,0 +1,234 @@ + + + diff --git a/ui/src/views/tool/gen/genInfoForm.vue b/ui/src/views/tool/gen/genInfoForm.vue new file mode 100644 index 0000000..0a1de16 --- /dev/null +++ b/ui/src/views/tool/gen/genInfoForm.vue @@ -0,0 +1,299 @@ + + + diff --git a/ui/src/views/tool/gen/importTable.vue b/ui/src/views/tool/gen/importTable.vue new file mode 100644 index 0000000..3ea9532 --- /dev/null +++ b/ui/src/views/tool/gen/importTable.vue @@ -0,0 +1,120 @@ + + + diff --git a/ui/src/views/tool/gen/index.vue b/ui/src/views/tool/gen/index.vue new file mode 100644 index 0000000..66bd13f --- /dev/null +++ b/ui/src/views/tool/gen/index.vue @@ -0,0 +1,348 @@ + + + diff --git a/ui/vue.config.js b/ui/vue.config.js new file mode 100644 index 0000000..0921752 --- /dev/null +++ b/ui/vue.config.js @@ -0,0 +1,136 @@ +'use strict' +const path = require('path') + +function resolve(dir) { + return path.join(__dirname, dir) +} + +const CompressionPlugin = require('compression-webpack-plugin') + +const name = process.env.VUE_APP_TITLE || 'RuoYi-Vue-Plus后台管理系统' // 网页标题 + +const port = process.env.port || process.env.npm_config_port || 80 // 端口 + +// vue.config.js 配置说明 +//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions +// 这里只列一部分,具体配置参考文档 +module.exports = { + // 部署生产环境和开发环境下的URL。 + // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上 + // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 + publicPath: process.env.VUE_APP_CONTEXT_PATH, + // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist) + outputDir: 'dist', + // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下) + assetsDir: 'static', + // 是否开启eslint保存检测,有效值:ture | false | 'error' + lintOnSave: process.env.NODE_ENV === 'development', + // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。 + productionSourceMap: false, + // webpack-dev-server 相关配置 + devServer: { + host: '0.0.0.0', + port: port, + open: true, + proxy: { + // detail: https://cli.vuejs.org/config/#devserver-proxy + [process.env.VUE_APP_BASE_API]: { + target: `http://localhost:8080`, + changeOrigin: true, + pathRewrite: { + ['^' + process.env.VUE_APP_BASE_API]: '' + } + } + }, + disableHostCheck: true + }, + css: { + loaderOptions: { + sass: { + sassOptions: { outputStyle: "expanded" } + } + } + }, + configureWebpack: { + name: name, + resolve: { + alias: { + '@': resolve('src') + } + }, + plugins: [ + // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件 + new CompressionPlugin({ + cache: false, // 不启用文件缓存 + test: /\.(js|css|html)?$/i, // 压缩文件格式 + filename: '[path].gz[query]', // 压缩后的文件名 + algorithm: 'gzip', // 使用gzip压缩 + minRatio: 0.8 // 压缩率小于1才会压缩 + }) + ], + }, + chainWebpack(config) { + config.plugins.delete('preload') // TODO: need test + config.plugins.delete('prefetch') // TODO: need test + + // set svg-sprite-loader + config.module + .rule('svg') + .exclude.add(resolve('src/assets/icons')) + .end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/assets/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + + config + .when(process.env.NODE_ENV !== 'development', + config => { + config + .plugin('ScriptExtHtmlWebpackPlugin') + .after('html') + .use('script-ext-html-webpack-plugin', [{ + // `runtime` must same as runtimeChunk name. default is `runtime` + inline: /runtime\..*\.js$/ + }]) + .end() + config + .optimization.splitChunks({ + chunks: 'all', + cacheGroups: { + libs: { + name: 'chunk-libs', + test: /[\\/]node_modules[\\/]/, + priority: 10, + chunks: 'initial' // only package third parties that are initially dependent + }, + elementUI: { + name: 'chunk-elementUI', // split elementUI into a single package + priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app + test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm + }, + commons: { + name: 'chunk-commons', + test: resolve('src/components'), // can customize your rules + minChunks: 3, // minimum common number + priority: 5, + reuseExistingChunk: true + } + } + }) + config.optimization.runtimeChunk('single'), + { + from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件 + to: './' //到根目录下 + } + } + ) + } +}