代码初始化

This commit is contained in:
wangxiangshun
2025-10-02 17:19:01 +08:00
commit 5cc31cfbbe
474 changed files with 53553 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*

47
.gitignore copy Normal file
View File

@@ -0,0 +1,47 @@
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
target/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# IDE
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
### NetBeans ###
nbproject/private/
build/*
nbbuild/
dist/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
!*/build/*.java
!*/build/*.html
!*/build/*.xml

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

19
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="vetti-admin" />
<module name="vetti-generator" />
<module name="vetti-quartz" />
<module name="vetti-system" />
<module name="vetti-framework" />
<module name="vetti-common" />
</profile>
</annotationProcessing>
</component>
</project>

19
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-admin/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-admin/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-framework/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-framework/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-generator/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-generator/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-quartz/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-quartz/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-system/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vetti-system/src/main/resources" charset="UTF-8" />
</component>
</project>

25
.idea/jarRepositories.xml generated Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="public" />
<option name="name" value="aliyun nexus" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

17
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
<option name="ignoredFiles">
<set>
<option value="$PROJECT_DIR$/vetti-command/pom.xml" />
</set>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="21" project-jdk-type="JavaSDK" />
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

20
LICENSE Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 RuoYi
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2
README.md Normal file
View File

@@ -0,0 +1,2 @@
# RouteZ-Service
Backend API services for the Vetti Dashboard Panel

BIN
output.mp3 Normal file

Binary file not shown.

346
pom.xml Normal file
View File

@@ -0,0 +1,346 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.vetti</groupId>
<artifactId>vetti-service</artifactId>
<version>3.9.0</version>
<name>haotaike</name>
<description>Vetti Dashboard</description>
<properties>
<vetti.version>3.9.0</vetti.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>21</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<spring-boot.version>2.5.15</spring-boot.version>
<druid.version>1.2.23</druid.version>
<bitwalker.version>1.21</bitwalker.version>
<swagger.version>3.0.0</swagger.version>
<swagger-models.version>1.6.2</swagger-models.version>
<kaptcha.version>2.3.3</kaptcha.version>
<pagehelper.boot.version>1.4.7</pagehelper.boot.version>
<fastjson.version>2.0.57</fastjson.version>
<oshi.version>6.8.2</oshi.version>
<commons.io.version>2.19.0</commons.io.version>
<commons.fileupload.version>1.6.0</commons.fileupload.version>
<poi.version>4.1.2</poi.version>
<velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version>
<!-- override dependency version -->
<tomcat.version>9.0.108</tomcat.version>
<logback.version>1.2.13</logback.version>
<spring-security.version>5.7.12</spring-security.version>
<spring-framework.version>5.3.39</spring-framework.version>
<spring-cloud.version>2021.0.9</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.6.1</spring-cloud-alibaba.version>
<lombok.version>1.18.30</lombok.version>
<hutool.version>5.8.38</hutool.version>
<okhttp3.version>4.12.0</okhttp3.version>
<minio.version>8.5.17</minio.version>
<sendgrid.version>4.10.3</sendgrid.version>
<gson.version>2.12.1</gson.version>
</properties>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- 覆盖SpringFramework的依赖配置-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring-framework.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 覆盖SpringSecurity的依赖配置-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>${spring-security.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 覆盖logback的依赖配置-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- 覆盖tomcat的依赖配置-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>${tomcat.version}</version>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${bitwalker.version}</version>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.boot.version}</version>
</dependency>
<!-- 获取系统信息 -->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>${oshi.version}</version>
</dependency>
<!-- Swagger3依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger-models.version}</version>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<!--lombok 工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
<!-- 糊涂工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- 定时任务-->
<dependency>
<groupId>com.vetti</groupId>
<artifactId>vetti-quartz</artifactId>
<version>${vetti.version}</version>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>com.vetti</groupId>
<artifactId>vetti-generator</artifactId>
<version>${vetti.version}</version>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.vetti</groupId>
<artifactId>vetti-framework</artifactId>
<version>${vetti.version}</version>
</dependency>
<!-- 系统模块-->
<dependency>
<groupId>com.vetti</groupId>
<artifactId>vetti-system</artifactId>
<version>${vetti.version}</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.vetti</groupId>
<artifactId>vetti-common</artifactId>
<version>${vetti.version}</version>
</dependency>
<!-- 4.x 版本,兼容 MinIO 8.x -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sendgrid</groupId>
<artifactId>sendgrid-java</artifactId>
<version>${sendgrid.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.14</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>vetti-admin</module>
<module>vetti-framework</module>
<module>vetti-system</module>
<module>vetti-quartz</module>
<module>vetti-generator</module>
<module>vetti-common</module>
</modules>
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

71
sql/table_20250828.sql Normal file
View File

@@ -0,0 +1,71 @@
CREATE TABLE `command_fleet_info` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '司机ID',
`codes` varchar(255) NOT NULL COMMENT '编号',
`name` varchar(255) NOT NULL COMMENT '车队名',
`status` varchar(32) 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(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='车队信息表';
CREATE TABLE `command_driver_info` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '司机ID',
`apple_id` varchar(255) NOT NULL COMMENT 'appleId',
`google_id` varchar(255) NOT NULL COMMENT '谷歌ID',
`emails` varchar(255) DEFAULT NULL COMMENT '邮箱',
`password` varchar(64) DEFAULT NULL COMMENT '密码',
`name` varchar(128) DEFAULT NULL COMMENT '姓名',
`avatar_url` varchar(255) DEFAULT NULL COMMENT '头像',
`work_status` varchar(32) DEFAULT NULL COMMENT '工作状态',
`active_time` decimal(10,2) DEFAULT NULL COMMENT '活跃时间(小时)',
`address` varchar(500) DEFAULT NULL COMMENT '地址',
`license_url` varchar(255) DEFAULT NULL COMMENT '执照图片',
`license_type` varchar(32) DEFAULT NULL COMMENT '执照类型',
`compliance_status` varchar(32) DEFAULT NULL 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(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='司机信息表';
CREATE TABLE `command_fleet_driver_info` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`driver_id` int NOT NULL COMMENT '司机ID',
`fleet_id` int NOT NULL COMMENT '车队ID',
`type` varchar(32) 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(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='车队与司机关联信息表';
CREATE TABLE `command_car_info` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`driver_id` int NOT NULL COMMENT '司机ID',
`car_no` varchar(64) NOT NULL COMMENT '车牌号',
`car_type` varchar(64) NOT NULL COMMENT '车辆类型',
`car_status` varchar(32) NOT NULL COMMENT '车辆状态',
`fuel` varchar(64) NOT NULL COMMENT '燃料百分比',
`kilometers` varchar(64) NOT NULL COMMENT '总公里数',
`current_location` varchar(255) NOT NULL COMMENT '当前地点',
`current_location_lng` varchar(64) NOT NULL COMMENT '当前地点经度',
`current_location_lat` varchar(64) NOT NULL COMMENT '当前地点纬度',
`destination_location` varchar(255) NOT NULL COMMENT '目的地点',
`destination_location_lng` varchar(64) NOT NULL COMMENT '目的地点经度',
`destination_location_lat` varchar(64) NOT NULL 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(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='车辆信息表';

106
vetti-admin/pom.xml Normal file
View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>vetti-service</artifactId>
<groupId>com.vetti</groupId>
<version>3.9.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>vetti-admin</artifactId>
<description>
web服务入口
</description>
<dependencies>
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- 表示依赖不会传递 -->
<optional>true</optional>
</dependency>
<!-- swagger3-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<!-- knife4j Swagger 增强版本 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.vetti</groupId>
<artifactId>vetti-framework</artifactId>
</dependency>
<!-- 定时任务-->
<dependency>
<groupId>com.vetti</groupId>
<artifactId>vetti-quartz</artifactId>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>com.vetti</groupId>
<artifactId>vetti-generator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>

View File

@@ -0,0 +1,23 @@
package com.vetti;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.web.servlet.ServletComponentScan;
/**
* 启动程序
*
* @author ruoyi
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@ServletComponentScan
public class RuoYiApplication
{
public static void main(String[] args)
{
SpringApplication.run(RuoYiApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ Vetti Dashboard Startup Successful ლ(´ڡ`ლ)゙ \n");
}
}

View File

@@ -0,0 +1,18 @@
package com.vetti;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* web容器中进行部署
*
* @author ruoyi
*/
public class RuoYiServletInitializer extends SpringBootServletInitializer
{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(RuoYiApplication.class);
}
}

View File

@@ -0,0 +1,24 @@
package com.vetti.config;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 放行白名单配置
*
* @author wangxiangshun
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "security.ignore")
public class IgnoreWhiteProperties
{
/**
* 放行白名单配置,网关不校验此处的白名单
*/
private List<String> whites = new ArrayList<>();
}

View File

@@ -0,0 +1,139 @@
package com.vetti.filter;
import com.vetti.common.constant.CacheConstants;
import com.vetti.common.constant.SecurityConstants;
import com.vetti.common.constant.TokenConstants;
import com.vetti.common.core.redis.RedisService;
import com.vetti.common.utils.JwtUtils;
import com.vetti.common.utils.MessageUtils;
import com.vetti.common.utils.StringUtils;
import com.vetti.config.IgnoreWhiteProperties;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* APP登录鉴权
*
* @author wangxiangshun
*/
@Component
@WebFilter
public class AuthFilter implements Filter
{
private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
// 排除过滤的 uri 地址
@Autowired
private IgnoreWhiteProperties ignoreWhite;
@Autowired
private RedisService redisService;
/**
* 获取缓存key
*/
private String getTokenKey(String token)
{
return CacheConstants.LOGIN_TOKEN_KEY + token;
}
/**
* 获取请求token
*/
private String getToken(HttpServletRequest request)
{
String token = request.getHeader(SecurityConstants.AUTHORIZATION_HEADER);
// 如果前端设置了令牌前缀,则裁剪掉前缀
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
{
token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
}
return token;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 执行拦截逻辑
String url = httpRequest.getRequestURI();
// 跳过不需要验证的路径
if ((url != null && url.contains("/v1/app"))
&& !StringUtils.matches(url, ignoreWhite.getWhites()))
{
String token = getToken(httpRequest);
if (StringUtils.isEmpty(token))
{
// 认证失败:返回 401
sendResponse(httpResponse,HttpServletResponse.SC_UNAUTHORIZED, MessageUtils.messageCustomize("systemExceptionAuthFilter10001"));
// 中断请求
return;
}
Claims claims = JwtUtils.parseToken(token);
if (claims == null)
{
// 认证失败:返回 401
sendResponse(httpResponse,HttpServletResponse.SC_UNAUTHORIZED,MessageUtils.messageCustomize("systemExceptionAuthFilter10002"));
// 中断请求
return;
}
String userkey = JwtUtils.getUserKey(claims);
boolean islogin = redisService.hasKey(getTokenKey(userkey));
if (!islogin)
{
// 认证失败:返回 401
sendResponse(httpResponse,HttpServletResponse.SC_UNAUTHORIZED,MessageUtils.messageCustomize("systemExceptionAuthFilter10003"));
// 中断请求
return;
}
String userid = JwtUtils.getUserId(claims);
String username = JwtUtils.getUserName(claims);
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))
{
// 认证失败:返回 401
sendResponse(httpResponse,HttpServletResponse.SC_UNAUTHORIZED,MessageUtils.messageCustomize("systemExceptionAuthFilter10004"));
// 中断请求
return;
}
}
// 拦截通过,继续执行后续过滤器或控制器
chain.doFilter(request, response);
}
/**
* 返回响应结构体
* @param response
* @param code HTTP状态码
* @param message 提示信息
* @throws IOException
*/
private void sendResponse(HttpServletResponse response,int code, String message) throws IOException {
// 设置HTTP状态码
response.setStatus(code);
// 设置响应内容类型JSON格式便于前端解析
response.setContentType("application/json;charset=UTF-8");
// 构建响应体(包含错误信息)
String jsonResponse = String.format(
"{\"code\": "+code+", \"msg\": \"%s\"}",
message
);
// 写入响应并关闭流
PrintWriter writer = response.getWriter();
writer.write(jsonResponse);
writer.flush();
writer.close();
}
}

View File

@@ -0,0 +1,94 @@
package com.vetti.web.controller.common;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.Producer;
import com.vetti.common.config.RuoYiConfig;
import com.vetti.common.constant.CacheConstants;
import com.vetti.common.constant.Constants;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.redis.RedisCache;
import com.vetti.common.utils.sign.Base64;
import com.vetti.common.utils.uuid.IdUtils;
import com.vetti.system.service.ISysConfigService;
/**
* 验证码操作处理
*
* @author ruoyi
*/
@RestController
public class CaptchaController
{
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
@Autowired
private RedisCache redisCache;
@Autowired
private ISysConfigService configService;
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public AjaxResult getCode(HttpServletResponse response) throws IOException
{
AjaxResult ajax = AjaxResult.success();
boolean captchaEnabled = configService.selectCaptchaEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled)
{
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
String capStr = null, code = null;
BufferedImage image = null;
// 生成验证码
String captchaType = RuoYiConfig.getCaptchaType();
if ("math".equals(captchaType))
{
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
}
else if ("char".equals(captchaType))
{
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
}
redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try
{
ImageIO.write(image, "jpg", os);
}
catch (IOException e)
{
return AjaxResult.error(e.getMessage());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
}

View File

@@ -0,0 +1,162 @@
package com.vetti.web.controller.common;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.vetti.common.config.RuoYiConfig;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.utils.StringUtils;
import com.vetti.common.utils.file.FileUploadUtils;
import com.vetti.common.utils.file.FileUtils;
import com.vetti.framework.config.ServerConfig;
/**
* 通用请求处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/common")
public class CommonController
{
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
@Autowired
private ServerConfig serverConfig;
private static final String FILE_DELIMETER = ",";
/**
* 通用下载请求
*
* @param fileName 文件名称
* @param delete 是否删除
*/
@GetMapping("/download")
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
{
try
{
if (!FileUtils.checkAllowDownload(fileName))
{
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = RuoYiConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, realFileName);
FileUtils.writeBytes(filePath, response.getOutputStream());
if (delete)
{
FileUtils.deleteFile(filePath);
}
}
catch (Exception e)
{
log.error("下载文件失败", e);
}
}
/**
* 通用上传请求(单个)
*/
@PostMapping("/upload")
public AjaxResult uploadFile(MultipartFile file) throws Exception
{
try
{
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
ajax.put("fileName", fileName);
ajax.put("newFileName", FileUtils.getName(fileName));
ajax.put("originalFilename", file.getOriginalFilename());
return ajax;
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
* 通用上传请求(多个)
*/
@PostMapping("/uploads")
public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
{
try
{
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
List<String> urls = new ArrayList<String>();
List<String> fileNames = new ArrayList<String>();
List<String> newFileNames = new ArrayList<String>();
List<String> originalFilenames = new ArrayList<String>();
for (MultipartFile file : files)
{
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
urls.add(url);
fileNames.add(fileName);
newFileNames.add(FileUtils.getName(fileName));
originalFilenames.add(file.getOriginalFilename());
}
AjaxResult ajax = AjaxResult.success();
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
return ajax;
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
* 本地资源通用下载
*/
@GetMapping("/download/resource")
public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
throws Exception
{
try
{
if (!FileUtils.checkAllowDownload(resource))
{
throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
}
// 本地资源路径
String localPath = RuoYiConfig.getProfile();
// 数据库资源地址
String downloadPath = localPath + FileUtils.stripPrefix(resource);
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, downloadName);
FileUtils.writeBytes(downloadPath, response.getOutputStream());
}
catch (Exception e)
{
log.error("下载文件失败", e);
}
}
}

View File

@@ -0,0 +1,121 @@
package com.vetti.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.constant.CacheConstants;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.utils.StringUtils;
import com.vetti.system.domain.SysCache;
/**
* 缓存监控
*
* @author ruoyi
*/
@RestController
@RequestMapping("/monitor/cache")
public class CacheController
{
@Autowired
private RedisTemplate<String, String> redisTemplate;
private final static List<SysCache> caches = new ArrayList<SysCache>();
{
caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
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(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception
{
Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
Map<String, Object> result = new HashMap<>(3);
result.put("info", info);
result.put("dbSize", dbSize);
List<Map<String, String>> pieList = new ArrayList<>();
commandStats.stringPropertyNames().forEach(key -> {
Map<String, String> 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 AjaxResult.success(result);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getNames")
public AjaxResult cache()
{
return AjaxResult.success(caches);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getKeys/{cacheName}")
public AjaxResult getCacheKeys(@PathVariable String cacheName)
{
Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
return AjaxResult.success(new TreeSet<>(cacheKeys));
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getValue/{cacheName}/{cacheKey}")
public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey)
{
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue);
return AjaxResult.success(sysCache);
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheName/{cacheName}")
public AjaxResult clearCacheName(@PathVariable String cacheName)
{
Collection<String> cacheKeys = redisTemplate.keys(cacheName + "*");
redisTemplate.delete(cacheKeys);
return AjaxResult.success();
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheKey/{cacheKey}")
public AjaxResult clearCacheKey(@PathVariable String cacheKey)
{
redisTemplate.delete(cacheKey);
return AjaxResult.success();
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheAll")
public AjaxResult clearCacheAll()
{
Collection<String> cacheKeys = redisTemplate.keys("*");
redisTemplate.delete(cacheKeys);
return AjaxResult.success();
}
}

View File

@@ -0,0 +1,27 @@
package com.vetti.web.controller.monitor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.framework.web.domain.Server;
/**
* 服务器监控
*
* @author ruoyi
*/
@RestController
@RequestMapping("/monitor/server")
public class ServerController
{
@PreAuthorize("@ss.hasPermi('monitor:server:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception
{
Server server = new Server();
server.copyTo();
return AjaxResult.success(server);
}
}

View File

@@ -0,0 +1,82 @@
package com.vetti.web.controller.monitor;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.poi.ExcelUtil;
import com.vetti.framework.web.service.SysPasswordService;
import com.vetti.system.domain.SysLogininfor;
import com.vetti.system.service.ISysLogininforService;
/**
* 系统访问记录
*
* @author ruoyi
*/
@RestController
@RequestMapping("/monitor/logininfor")
public class SysLogininforController extends BaseController
{
@Autowired
private ISysLogininforService logininforService;
@Autowired
private SysPasswordService passwordService;
@PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
@GetMapping("/list")
public TableDataInfo list(SysLogininfor logininfor)
{
startPage();
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
return getDataTable(list);
}
@Log(title = "登录日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysLogininfor logininfor)
{
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
util.exportExcel(response, list, "登录日志");
}
@PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
@Log(title = "登录日志", businessType = BusinessType.DELETE)
@DeleteMapping("/{infoIds}")
public AjaxResult remove(@PathVariable Long[] infoIds)
{
return toAjax(logininforService.deleteLogininforByIds(infoIds));
}
@PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
@Log(title = "登录日志", businessType = BusinessType.CLEAN)
@DeleteMapping("/clean")
public AjaxResult clean()
{
logininforService.cleanLogininfor();
return success();
}
@PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')")
@Log(title = "账户解锁", businessType = BusinessType.OTHER)
@GetMapping("/unlock/{userName}")
public AjaxResult unlock(@PathVariable("userName") String userName)
{
passwordService.clearLoginRecordCache(userName);
return success();
}
}

View File

@@ -0,0 +1,69 @@
package com.vetti.web.controller.monitor;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.poi.ExcelUtil;
import com.vetti.system.domain.SysOperLog;
import com.vetti.system.service.ISysOperLogService;
/**
* 操作日志记录
*
* @author ruoyi
*/
@RestController
@RequestMapping("/monitor/operlog")
public class SysOperlogController extends BaseController
{
@Autowired
private ISysOperLogService operLogService;
@PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
@GetMapping("/list")
public TableDataInfo list(SysOperLog operLog)
{
startPage();
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
return getDataTable(list);
}
@Log(title = "操作日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysOperLog operLog)
{
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
util.exportExcel(response, list, "操作日志");
}
@Log(title = "操作日志", businessType = BusinessType.DELETE)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/{operIds}")
public AjaxResult remove(@PathVariable Long[] operIds)
{
return toAjax(operLogService.deleteOperLogByIds(operIds));
}
@Log(title = "操作日志", businessType = BusinessType.CLEAN)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/clean")
public AjaxResult clean()
{
operLogService.cleanOperLog();
return success();
}
}

View File

@@ -0,0 +1,83 @@
package com.vetti.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.constant.CacheConstants;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.model.LoginUser;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.core.redis.RedisCache;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.StringUtils;
import com.vetti.system.domain.SysUserOnline;
import com.vetti.system.service.ISysUserOnlineService;
/**
* 在线用户监控
*
* @author ruoyi
*/
@RestController
@RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController
{
@Autowired
private ISysUserOnlineService userOnlineService;
@Autowired
private RedisCache redisCache;
@PreAuthorize("@ss.hasPermi('monitor:online:list')")
@GetMapping("/list")
public TableDataInfo list(String ipaddr, String userName)
{
Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
for (String key : keys)
{
LoginUser user = redisCache.getCacheObject(key);
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
{
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
}
else if (StringUtils.isNotEmpty(ipaddr))
{
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
}
else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
{
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
}
else
{
userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
}
}
Collections.reverse(userOnlineList);
userOnlineList.removeAll(Collections.singleton(null));
return getDataTable(userOnlineList);
}
/**
* 强退用户
*/
@PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@DeleteMapping("/{tokenId}")
public AjaxResult forceLogout(@PathVariable String tokenId)
{
redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId);
return success();
}
}

View File

@@ -0,0 +1,133 @@
package com.vetti.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.poi.ExcelUtil;
import com.vetti.system.domain.SysConfig;
import com.vetti.system.service.ISysConfigService;
/**
* 参数配置 信息操作处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/config")
public class SysConfigController extends BaseController
{
@Autowired
private ISysConfigService configService;
/**
* 获取参数配置列表
*/
@PreAuthorize("@ss.hasPermi('system:config:list')")
@GetMapping("/list")
public TableDataInfo list(SysConfig config)
{
startPage();
List<SysConfig> list = configService.selectConfigList(config);
return getDataTable(list);
}
@Log(title = "参数管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:config:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysConfig config)
{
List<SysConfig> list = configService.selectConfigList(config);
ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
util.exportExcel(response, list, "参数数据");
}
/**
* 根据参数编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:config:query')")
@GetMapping(value = "/{configId}")
public AjaxResult getInfo(@PathVariable Long configId)
{
return success(configService.selectConfigById(configId));
}
/**
* 根据参数键名查询参数值
*/
@GetMapping(value = "/configKey/{configKey}")
public AjaxResult getConfigKey(@PathVariable String configKey)
{
return success(configService.selectConfigByKey(configKey));
}
/**
* 新增参数配置
*/
@PreAuthorize("@ss.hasPermi('system:config:add')")
@Log(title = "参数管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysConfig config)
{
if (!configService.checkConfigKeyUnique(config))
{
return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setCreateBy(getUsername());
return toAjax(configService.insertConfig(config));
}
/**
* 修改参数配置
*/
@PreAuthorize("@ss.hasPermi('system:config:edit')")
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysConfig config)
{
if (!configService.checkConfigKeyUnique(config))
{
return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setUpdateBy(getUsername());
return toAjax(configService.updateConfig(config));
}
/**
* 删除参数配置
*/
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "参数管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{configIds}")
public AjaxResult remove(@PathVariable Long[] configIds)
{
configService.deleteConfigByIds(configIds);
return success();
}
/**
* 刷新参数缓存
*/
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "参数管理", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public AjaxResult refreshCache()
{
configService.resetConfigCache();
return success();
}
}

View File

@@ -0,0 +1,132 @@
package com.vetti.web.controller.system;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.constant.UserConstants;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.entity.SysDept;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.StringUtils;
import com.vetti.system.service.ISysDeptService;
/**
* 部门信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/dept")
public class SysDeptController extends BaseController
{
@Autowired
private ISysDeptService deptService;
/**
* 获取部门列表
*/
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list")
public AjaxResult list(SysDept dept)
{
List<SysDept> depts = deptService.selectDeptList(dept);
return success(depts);
}
/**
* 查询部门列表(排除节点)
*/
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list/exclude/{deptId}")
public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
{
List<SysDept> depts = deptService.selectDeptList(new SysDept());
depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
return success(depts);
}
/**
* 根据部门编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:dept:query')")
@GetMapping(value = "/{deptId}")
public AjaxResult getInfo(@PathVariable Long deptId)
{
deptService.checkDeptDataScope(deptId);
return success(deptService.selectDeptById(deptId));
}
/**
* 新增部门
*/
@PreAuthorize("@ss.hasPermi('system:dept:add')")
@Log(title = "部门管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDept dept)
{
if (!deptService.checkDeptNameUnique(dept))
{
return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
dept.setCreateBy(getUsername());
return toAjax(deptService.insertDept(dept));
}
/**
* 修改部门
*/
@PreAuthorize("@ss.hasPermi('system:dept:edit')")
@Log(title = "部门管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDept dept)
{
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (!deptService.checkDeptNameUnique(dept))
{
return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
else if (dept.getParentId().equals(deptId))
{
return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
}
else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0)
{
return error("该部门包含未停用的子部门!");
}
dept.setUpdateBy(getUsername());
return toAjax(deptService.updateDept(dept));
}
/**
* 删除部门
*/
@PreAuthorize("@ss.hasPermi('system:dept:remove')")
@Log(title = "部门管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{deptId}")
public AjaxResult remove(@PathVariable Long deptId)
{
if (deptService.hasChildByDeptId(deptId))
{
return warn("存在下级部门,不允许删除");
}
if (deptService.checkDeptExistUser(deptId))
{
return warn("部门存在用户,不允许删除");
}
deptService.checkDeptDataScope(deptId);
return toAjax(deptService.deleteDeptById(deptId));
}
}

View File

@@ -0,0 +1,121 @@
package com.vetti.web.controller.system;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.entity.SysDictData;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.StringUtils;
import com.vetti.common.utils.poi.ExcelUtil;
import com.vetti.system.service.ISysDictDataService;
import com.vetti.system.service.ISysDictTypeService;
/**
* 数据字典信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController extends BaseController
{
@Autowired
private ISysDictDataService dictDataService;
@Autowired
private ISysDictTypeService dictTypeService;
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictData dictData)
{
startPage();
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
return getDataTable(list);
}
@Log(title = "字典数据", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:dict:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysDictData dictData)
{
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
util.exportExcel(response, list, "字典数据");
}
/**
* 查询字典数据详细
*/
@PreAuthorize("@ss.hasPermi('system:dict:query')")
@GetMapping(value = "/{dictCode}")
public AjaxResult getInfo(@PathVariable Long dictCode)
{
return success(dictDataService.selectDictDataById(dictCode));
}
/**
* 根据字典类型查询字典数据信息
*/
@GetMapping(value = "/type/{dictType}")
public AjaxResult dictType(@PathVariable String dictType)
{
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
if (StringUtils.isNull(data))
{
data = new ArrayList<SysDictData>();
}
return success(data);
}
/**
* 新增字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictData dict)
{
dict.setCreateBy(getUsername());
return toAjax(dictDataService.insertDictData(dict));
}
/**
* 修改保存字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictData dict)
{
dict.setUpdateBy(getUsername());
return toAjax(dictDataService.updateDictData(dict));
}
/**
* 删除字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictCodes}")
public AjaxResult remove(@PathVariable Long[] dictCodes)
{
dictDataService.deleteDictDataByIds(dictCodes);
return success();
}
}

View File

@@ -0,0 +1,131 @@
package com.vetti.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.entity.SysDictType;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.poi.ExcelUtil;
import com.vetti.system.service.ISysDictTypeService;
/**
* 数据字典信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/dict/type")
public class SysDictTypeController extends BaseController
{
@Autowired
private ISysDictTypeService dictTypeService;
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictType dictType)
{
startPage();
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
return getDataTable(list);
}
@Log(title = "字典类型", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:dict:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysDictType dictType)
{
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
util.exportExcel(response, list, "字典类型");
}
/**
* 查询字典类型详细
*/
@PreAuthorize("@ss.hasPermi('system:dict:query')")
@GetMapping(value = "/{dictId}")
public AjaxResult getInfo(@PathVariable Long dictId)
{
return success(dictTypeService.selectDictTypeById(dictId));
}
/**
* 新增字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典类型", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictType dict)
{
if (!dictTypeService.checkDictTypeUnique(dict))
{
return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setCreateBy(getUsername());
return toAjax(dictTypeService.insertDictType(dict));
}
/**
* 修改字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典类型", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictType dict)
{
if (!dictTypeService.checkDictTypeUnique(dict))
{
return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setUpdateBy(getUsername());
return toAjax(dictTypeService.updateDictType(dict));
}
/**
* 删除字典类型
*/
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictIds}")
public AjaxResult remove(@PathVariable Long[] dictIds)
{
dictTypeService.deleteDictTypeByIds(dictIds);
return success();
}
/**
* 刷新字典缓存
*/
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public AjaxResult refreshCache()
{
dictTypeService.resetDictCache();
return success();
}
/**
* 获取字典选择框列表
*/
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
return success(dictTypes);
}
}

View File

@@ -0,0 +1,38 @@
package com.vetti.web.controller.system;
import com.vetti.common.annotation.Anonymous;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.web.service.ISystemI18nGainService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
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;
/**
* 系统获取国际化信息
*
* @author Wangxiangshun
*/
@Api(tags ="国际化管理")
@RestController
@RequestMapping("/system/i18nGain")
public class SysI18nGainController extends BaseController {
@Autowired
private ISystemI18nGainService systemI18nGainService;
/**
* 获取国际化信息
*/
@Anonymous
@ApiOperation("查询国际化信息")
@GetMapping(value = "/getInfo")
public AjaxResult getInfo()
{
return AjaxResult.success(systemI18nGainService.getI18nInfo());
}
}

View File

@@ -0,0 +1,29 @@
package com.vetti.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.config.RuoYiConfig;
import com.vetti.common.utils.StringUtils;
/**
* 首页
*
* @author ruoyi
*/
@RestController
public class SysIndexController
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/**
* 访问首页,提示语
*/
@RequestMapping("/")
public String index()
{
return StringUtils.format("欢迎使用{}后台管理框架当前版本v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
}
}

View File

@@ -0,0 +1,131 @@
package com.vetti.web.controller.system;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
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 com.vetti.common.constant.Constants;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.entity.SysMenu;
import com.vetti.common.core.domain.entity.SysUser;
import com.vetti.common.core.domain.model.LoginBody;
import com.vetti.common.core.domain.model.LoginUser;
import com.vetti.common.core.text.Convert;
import com.vetti.common.utils.DateUtils;
import com.vetti.common.utils.SecurityUtils;
import com.vetti.common.utils.StringUtils;
import com.vetti.framework.web.service.SysLoginService;
import com.vetti.framework.web.service.SysPermissionService;
import com.vetti.framework.web.service.TokenService;
import com.vetti.system.service.ISysConfigService;
import com.vetti.system.service.ISysMenuService;
/**
* 登录验证
*
* @author ruoyi
*/
@RestController
public class SysLoginController
{
@Autowired
private SysLoginService loginService;
@Autowired
private ISysMenuService menuService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private TokenService tokenService;
@Autowired
private ISysConfigService configService;
/**
* 登录方法
*
* @param loginBody 登录信息
* @return 结果
*/
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
{
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;
}
/**
* 获取用户信息
*
* @return 用户信息
*/
@GetMapping("getInfo")
public AjaxResult getInfo()
{
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
// 角色集合
Set<String> roles = permissionService.getRolePermission(user);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(user);
if (!loginUser.getPermissions().equals(permissions))
{
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
AjaxResult ajax = AjaxResult.success();
ajax.put("user", user);
ajax.put("roles", roles);
ajax.put("permissions", permissions);
ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
return ajax;
}
/**
* 获取路由信息
*
* @return 路由信息
*/
@GetMapping("getRouters")
public AjaxResult getRouters()
{
Long userId = SecurityUtils.getUserId();
List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
return AjaxResult.success(menuService.buildMenus(menus));
}
// 检查初始密码是否提醒修改
public boolean initPasswordIsModify(Date pwdUpdateDate)
{
Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
}
// 检查密码是否过期
public boolean passwordIsExpiration(Date pwdUpdateDate)
{
Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
if (passwordValidateDays != null && passwordValidateDays > 0)
{
if (StringUtils.isNull(pwdUpdateDate))
{
// 如果从未修改过初始密码,直接提醒过期
return true;
}
Date nowDate = DateUtils.getNowDate();
return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
}
return false;
}
}

View File

@@ -0,0 +1,142 @@
package com.vetti.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.constant.UserConstants;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.entity.SysMenu;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.StringUtils;
import com.vetti.system.service.ISysMenuService;
/**
* 菜单信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController
{
@Autowired
private ISysMenuService menuService;
/**
* 获取菜单列表
*/
@PreAuthorize("@ss.hasPermi('system:menu:list')")
@GetMapping("/list")
public AjaxResult list(SysMenu menu)
{
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return success(menus);
}
/**
* 根据菜单编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:menu:query')")
@GetMapping(value = "/{menuId}")
public AjaxResult getInfo(@PathVariable Long menuId)
{
return success(menuService.selectMenuById(menuId));
}
/**
* 获取菜单下拉树列表
*/
@GetMapping("/treeselect")
public AjaxResult treeselect(SysMenu menu)
{
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return success(menuService.buildMenuTreeSelect(menus));
}
/**
* 加载对应角色菜单列表树
*/
@GetMapping(value = "/roleMenuTreeselect/{roleId}")
public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId)
{
List<SysMenu> menus = menuService.selectMenuList(getUserId());
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
ajax.put("menus", menuService.buildMenuTreeSelect(menus));
return ajax;
}
/**
* 新增菜单
*/
@PreAuthorize("@ss.hasPermi('system:menu:add')")
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu)
{
if (!menuService.checkMenuNameUnique(menu))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
{
return error("新增菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}
menu.setCreateBy(getUsername());
return toAjax(menuService.insertMenu(menu));
}
/**
* 修改菜单
*/
@PreAuthorize("@ss.hasPermi('system:menu:edit')")
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysMenu menu)
{
if (!menuService.checkMenuNameUnique(menu))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
{
return error("修改菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}
else if (menu.getMenuId().equals(menu.getParentId()))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
}
menu.setUpdateBy(getUsername());
return toAjax(menuService.updateMenu(menu));
}
/**
* 删除菜单
*/
@PreAuthorize("@ss.hasPermi('system:menu:remove')")
@Log(title = "菜单管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{menuId}")
public AjaxResult remove(@PathVariable("menuId") Long menuId)
{
if (menuService.hasChildByMenuId(menuId))
{
return warn("存在子菜单,不允许删除");
}
if (menuService.checkMenuExistRole(menuId))
{
return warn("菜单已分配,不允许删除");
}
return toAjax(menuService.deleteMenuById(menuId));
}
}

View File

@@ -0,0 +1,91 @@
package com.vetti.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.system.domain.SysNotice;
import com.vetti.system.service.ISysNoticeService;
/**
* 公告 信息操作处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/notice")
public class SysNoticeController extends BaseController
{
@Autowired
private ISysNoticeService noticeService;
/**
* 获取通知公告列表
*/
@PreAuthorize("@ss.hasPermi('system:notice:list')")
@GetMapping("/list")
public TableDataInfo list(SysNotice notice)
{
startPage();
List<SysNotice> list = noticeService.selectNoticeList(notice);
return getDataTable(list);
}
/**
* 根据通知公告编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:notice:query')")
@GetMapping(value = "/{noticeId}")
public AjaxResult getInfo(@PathVariable Long noticeId)
{
return success(noticeService.selectNoticeById(noticeId));
}
/**
* 新增通知公告
*/
@PreAuthorize("@ss.hasPermi('system:notice:add')")
@Log(title = "通知公告", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysNotice notice)
{
notice.setCreateBy(getUsername());
return toAjax(noticeService.insertNotice(notice));
}
/**
* 修改通知公告
*/
@PreAuthorize("@ss.hasPermi('system:notice:edit')")
@Log(title = "通知公告", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysNotice notice)
{
notice.setUpdateBy(getUsername());
return toAjax(noticeService.updateNotice(notice));
}
/**
* 删除通知公告
*/
@PreAuthorize("@ss.hasPermi('system:notice:remove')")
@Log(title = "通知公告", businessType = BusinessType.DELETE)
@DeleteMapping("/{noticeIds}")
public AjaxResult remove(@PathVariable Long[] noticeIds)
{
return toAjax(noticeService.deleteNoticeByIds(noticeIds));
}
}

View File

@@ -0,0 +1,129 @@
package com.vetti.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.poi.ExcelUtil;
import com.vetti.system.domain.SysPost;
import com.vetti.system.service.ISysPostService;
/**
* 岗位信息操作处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/post")
public class SysPostController extends BaseController
{
@Autowired
private ISysPostService postService;
/**
* 获取岗位列表
*/
@PreAuthorize("@ss.hasPermi('system:post:list')")
@GetMapping("/list")
public TableDataInfo list(SysPost post)
{
startPage();
List<SysPost> list = postService.selectPostList(post);
return getDataTable(list);
}
@Log(title = "岗位管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:post:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysPost post)
{
List<SysPost> list = postService.selectPostList(post);
ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
util.exportExcel(response, list, "岗位数据");
}
/**
* 根据岗位编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:post:query')")
@GetMapping(value = "/{postId}")
public AjaxResult getInfo(@PathVariable Long postId)
{
return success(postService.selectPostById(postId));
}
/**
* 新增岗位
*/
@PreAuthorize("@ss.hasPermi('system:post:add')")
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysPost post)
{
if (!postService.checkPostNameUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (!postService.checkPostCodeUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setCreateBy(getUsername());
return toAjax(postService.insertPost(post));
}
/**
* 修改岗位
*/
@PreAuthorize("@ss.hasPermi('system:post:edit')")
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysPost post)
{
if (!postService.checkPostNameUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (!postService.checkPostCodeUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setUpdateBy(getUsername());
return toAjax(postService.updatePost(post));
}
/**
* 删除岗位
*/
@PreAuthorize("@ss.hasPermi('system:post:remove')")
@Log(title = "岗位管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{postIds}")
public AjaxResult remove(@PathVariable Long[] postIds)
{
return toAjax(postService.deletePostByIds(postIds));
}
/**
* 获取岗位选择框列表
*/
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
List<SysPost> posts = postService.selectPostAll();
return success(posts);
}
}

View File

@@ -0,0 +1,148 @@
package com.vetti.web.controller.system;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.vetti.common.annotation.Log;
import com.vetti.common.config.RuoYiConfig;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.entity.SysUser;
import com.vetti.common.core.domain.model.LoginUser;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.DateUtils;
import com.vetti.common.utils.SecurityUtils;
import com.vetti.common.utils.StringUtils;
import com.vetti.common.utils.file.FileUploadUtils;
import com.vetti.common.utils.file.FileUtils;
import com.vetti.common.utils.file.MimeTypeUtils;
import com.vetti.framework.web.service.TokenService;
import com.vetti.system.service.ISysUserService;
/**
* 个人信息 业务处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/user/profile")
public class SysProfileController extends BaseController
{
@Autowired
private ISysUserService userService;
@Autowired
private TokenService tokenService;
/**
* 个人信息
*/
@GetMapping
public AjaxResult profile()
{
LoginUser loginUser = getLoginUser();
SysUser user = loginUser.getUser();
AjaxResult ajax = AjaxResult.success(user);
ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
return ajax;
}
/**
* 修改用户
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult updateProfile(@RequestBody SysUser user)
{
LoginUser loginUser = getLoginUser();
SysUser currentUser = loginUser.getUser();
currentUser.setNickName(user.getNickName());
currentUser.setEmail(user.getEmail());
currentUser.setPhonenumber(user.getPhonenumber());
currentUser.setSex(user.getSex());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
{
return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
{
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
}
if (userService.updateUserProfile(currentUser) > 0)
{
// 更新缓存用户信息
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改个人信息异常,请联系管理员");
}
/**
* 重置密码
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
{
String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser();
Long userId = loginUser.getUserId();
String password = loginUser.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password))
{
return error("修改密码失败,旧密码错误");
}
if (SecurityUtils.matchesPassword(newPassword, password))
{
return error("新密码不能与旧密码相同");
}
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userId, newPassword) > 0)
{
// 更新缓存用户密码&密码最后更新时间
loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改密码异常,请联系管理员");
}
/**
* 头像上传
*/
@Log(title = "用户头像", businessType = BusinessType.UPDATE)
@PostMapping("/avatar")
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception
{
if (!file.isEmpty())
{
LoginUser loginUser = getLoginUser();
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
if (userService.updateUserAvatar(loginUser.getUserId(), avatar))
{
String oldAvatar = loginUser.getUser().getAvatar();
if (StringUtils.isNotEmpty(oldAvatar))
{
FileUtils.deleteFile(RuoYiConfig.getProfile() + FileUtils.stripPrefix(oldAvatar));
}
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", avatar);
// 更新缓存用户头像
loginUser.getUser().setAvatar(avatar);
tokenService.setLoginUser(loginUser);
return ajax;
}
}
return error("上传图片异常,请联系管理员");
}
}

View File

@@ -0,0 +1,38 @@
package com.vetti.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.model.RegisterBody;
import com.vetti.common.utils.StringUtils;
import com.vetti.framework.web.service.SysRegisterService;
import com.vetti.system.service.ISysConfigService;
/**
* 注册验证
*
* @author ruoyi
*/
@RestController
public class SysRegisterController extends BaseController
{
@Autowired
private SysRegisterService registerService;
@Autowired
private ISysConfigService configService;
@PostMapping("/register")
public AjaxResult register(@RequestBody RegisterBody user)
{
if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser"))))
{
return error("当前系统没有开启注册功能!");
}
String msg = registerService.register(user);
return StringUtils.isEmpty(msg) ? success() : error(msg);
}
}

View File

@@ -0,0 +1,262 @@
package com.vetti.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.entity.SysDept;
import com.vetti.common.core.domain.entity.SysRole;
import com.vetti.common.core.domain.entity.SysUser;
import com.vetti.common.core.domain.model.LoginUser;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.StringUtils;
import com.vetti.common.utils.poi.ExcelUtil;
import com.vetti.framework.web.service.SysPermissionService;
import com.vetti.framework.web.service.TokenService;
import com.vetti.system.domain.SysUserRole;
import com.vetti.system.service.ISysDeptService;
import com.vetti.system.service.ISysRoleService;
import com.vetti.system.service.ISysUserService;
/**
* 角色信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/role")
public class SysRoleController extends BaseController
{
@Autowired
private ISysRoleService roleService;
@Autowired
private TokenService tokenService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private ISysUserService userService;
@Autowired
private ISysDeptService deptService;
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/list")
public TableDataInfo list(SysRole role)
{
startPage();
List<SysRole> list = roleService.selectRoleList(role);
return getDataTable(list);
}
@Log(title = "角色管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:role:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysRole role)
{
List<SysRole> list = roleService.selectRoleList(role);
ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
util.exportExcel(response, list, "角色数据");
}
/**
* 根据角色编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/{roleId}")
public AjaxResult getInfo(@PathVariable Long roleId)
{
roleService.checkRoleDataScope(roleId);
return success(roleService.selectRoleById(roleId));
}
/**
* 新增角色
*/
@PreAuthorize("@ss.hasPermi('system:role:add')")
@Log(title = "角色管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysRole role)
{
if (!roleService.checkRoleNameUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (!roleService.checkRoleKeyUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setCreateBy(getUsername());
return toAjax(roleService.insertRole(role));
}
/**
* 修改保存角色
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (!roleService.checkRoleNameUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (!roleService.checkRoleKeyUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setUpdateBy(getUsername());
if (roleService.updateRole(role) > 0)
{
// 更新缓存用户权限
LoginUser loginUser = getLoginUser();
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
{
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
tokenService.setLoginUser(loginUser);
}
return success();
}
return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
}
/**
* 修改保存数据权限
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/dataScope")
public AjaxResult dataScope(@RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
return toAjax(roleService.authDataScope(role));
}
/**
* 状态修改
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
role.setUpdateBy(getUsername());
return toAjax(roleService.updateRoleStatus(role));
}
/**
* 删除角色
*/
@PreAuthorize("@ss.hasPermi('system:role:remove')")
@Log(title = "角色管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{roleIds}")
public AjaxResult remove(@PathVariable Long[] roleIds)
{
return toAjax(roleService.deleteRoleByIds(roleIds));
}
/**
* 获取角色选择框列表
*/
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping("/optionselect")
public AjaxResult optionselect()
{
return success(roleService.selectRoleAll());
}
/**
* 查询已分配用户角色列表
*/
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/allocatedList")
public TableDataInfo allocatedList(SysUser user)
{
startPage();
List<SysUser> list = userService.selectAllocatedList(user);
return getDataTable(list);
}
/**
* 查询未分配用户角色列表
*/
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/unallocatedList")
public TableDataInfo unallocatedList(SysUser user)
{
startPage();
List<SysUser> list = userService.selectUnallocatedList(user);
return getDataTable(list);
}
/**
* 取消授权用户
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancel")
public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole)
{
return toAjax(roleService.deleteAuthUser(userRole));
}
/**
* 批量取消授权用户
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancelAll")
public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds)
{
return toAjax(roleService.deleteAuthUsers(roleId, userIds));
}
/**
* 批量选择用户授权
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/selectAll")
public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds)
{
roleService.checkRoleDataScope(roleId);
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
/**
* 获取对应角色部门树列表
*/
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/deptTree/{roleId}")
public AjaxResult deptTree(@PathVariable("roleId") Long roleId)
{
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
return ajax;
}
}

View File

@@ -0,0 +1,266 @@
package com.vetti.web.controller.system;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.vetti.common.annotation.Log;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.entity.SysDept;
import com.vetti.common.core.domain.entity.SysRole;
import com.vetti.common.core.domain.entity.SysUser;
import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.utils.SecurityUtils;
import com.vetti.common.utils.StringUtils;
import com.vetti.common.utils.poi.ExcelUtil;
import com.vetti.system.service.ISysDeptService;
import com.vetti.system.service.ISysPostService;
import com.vetti.system.service.ISysRoleService;
import com.vetti.system.service.ISysUserService;
/**
* 用户信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController
{
@Autowired
private ISysUserService userService;
@Autowired
private ISysRoleService roleService;
@Autowired
private ISysDeptService deptService;
@Autowired
private ISysPostService postService;
/**
* 获取用户列表
*/
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user)
{
startPage();
List<SysUser> list = userService.selectUserList(user);
return getDataTable(list);
}
/**
* 获取用户列表
*/
@GetMapping("/getList")
public AjaxResult getList(SysUser user)
{
List<SysUser> list = userService.selectUserList(user);
return AjaxResult.success(list);
}
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysUser user)
{
List<SysUser> list = userService.selectUserList(user);
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
util.exportExcel(response, list, "用户数据");
}
@Log(title = "用户管理", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('system:user:import')")
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
List<SysUser> userList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = userService.importUser(userList, updateSupport, operName);
return success(message);
}
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
util.importTemplateExcel(response, "用户数据");
}
/**
* 根据用户编号获取详细信息
*/
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
{
AjaxResult ajax = AjaxResult.success();
if (StringUtils.isNotNull(userId))
{
userService.checkUserDataScope(userId);
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
/**
* 新增用户
*/
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user)
{
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setCreateBy(getUsername());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
return toAjax(userService.insertUser(user));
}
/**
* 修改用户
*/
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setUpdateBy(getUsername());
return toAjax(userService.updateUser(user));
}
/**
* 删除用户
*/
@PreAuthorize("@ss.hasPermi('system:user:remove')")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable Long[] userIds)
{
if (ArrayUtils.contains(userIds, getUserId()))
{
return error("当前用户不能删除");
}
return toAjax(userService.deleteUserByIds(userIds));
}
/**
* 重置密码
*/
@PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetPwd")
public AjaxResult resetPwd(@RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
user.setUpdateBy(getUsername());
return toAjax(userService.resetPwd(user));
}
/**
* 状态修改
*/
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setUpdateBy(getUsername());
return toAjax(userService.updateUserStatus(user));
}
/**
* 根据用户编号获取授权角色
*/
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping("/authRole/{userId}")
public AjaxResult authRole(@PathVariable("userId") Long userId)
{
AjaxResult ajax = AjaxResult.success();
SysUser user = userService.selectUserById(userId);
List<SysRole> roles = roleService.selectRolesByUserId(userId);
ajax.put("user", user);
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
return ajax;
}
/**
* 用户授权角色
*/
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PutMapping("/authRole")
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.checkUserDataScope(userId);
roleService.checkRoleDataScope(roleIds);
userService.insertUserAuth(userId, roleIds);
return success();
}
/**
* 获取部门树列表
*/
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/deptTree")
public AjaxResult deptTree(SysDept dept)
{
return success(deptService.selectDeptTreeList(dept));
}
}

View File

@@ -0,0 +1,24 @@
package com.vetti.web.controller.tool;
import com.vetti.common.core.controller.BaseController;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* swagger 接口
*
* @author ruoyi
*/
@Controller
@RequestMapping("/tool/swagger")
public class SwaggerController extends BaseController
{
@PreAuthorize("@ss.hasPermi('tool:swagger:view')")
@GetMapping()
public String index()
{
return redirect("/doc.html");
}
}

View File

@@ -0,0 +1,183 @@
package com.vetti.web.controller.tool;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.R;
import com.vetti.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
/**
* swagger 用户测试方法
*
* @author ruoyi
*/
@Api("用户信息管理")
@RestController
@RequestMapping("/test/user")
public class TestController extends BaseController
{
private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>();
{
users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
}
@ApiOperation("获取用户列表")
@GetMapping("/list")
public R<List<UserEntity>> userList()
{
List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
return R.ok(userList);
}
@ApiOperation("获取用户详细")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@GetMapping("/{userId}")
public R<UserEntity> getUser(@PathVariable Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
return R.ok(users.get(userId));
}
else
{
return R.fail("用户不存在");
}
}
@ApiOperation("新增用户")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
@ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
@ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
})
@PostMapping("/save")
public R<String> save(UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
return R.fail("用户ID不能为空");
}
users.put(user.getUserId(), user);
return R.ok();
}
@ApiOperation("更新用户")
@PutMapping("/update")
public R<String> update(@RequestBody UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
return R.fail("用户ID不能为空");
}
if (users.isEmpty() || !users.containsKey(user.getUserId()))
{
return R.fail("用户不存在");
}
users.remove(user.getUserId());
users.put(user.getUserId(), user);
return R.ok();
}
@ApiOperation("删除用户信息")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@DeleteMapping("/{userId}")
public R<String> delete(@PathVariable Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
users.remove(userId);
return R.ok();
}
else
{
return R.fail("用户不存在");
}
}
}
@ApiModel(value = "UserEntity", description = "用户实体")
class UserEntity
{
@ApiModelProperty("用户ID")
private Integer userId;
@ApiModelProperty("用户名称")
private String username;
@ApiModelProperty("用户密码")
private String password;
@ApiModelProperty("用户手机")
private String mobile;
public UserEntity()
{
}
public UserEntity(Integer userId, String username, String password, String mobile)
{
this.userId = userId;
this.username = username;
this.password = password;
this.mobile = mobile;
}
public Integer getUserId()
{
return userId;
}
public void setUserId(Integer userId)
{
this.userId = userId;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getMobile()
{
return mobile;
}
public void setMobile(String mobile)
{
this.mobile = mobile;
}
}

View File

@@ -0,0 +1,124 @@
package com.vetti.web.core.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.vetti.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
/**
* Swagger2的接口配置
*
* @author ruoyi
*/
@Configuration
public class SwaggerConfig
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/** 是否开启swagger */
@Value("${swagger.enabled}")
private boolean enabled;
/** 设置请求的统一前缀 */
@Value("${swagger.pathMapping}")
private String pathMapping;
/**
* 创建API
*/
@Bean
public Docket createRestApi()
{
return new Docket(DocumentationType.OAS_30)
// 是否启用Swagger
.enable(enabled)
// 用来创建该API的基本信息展示在文档的页面中自定义展示的信息
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
/* 设置安全模式swagger可以设置访问token */
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.pathMapping(pathMapping);
}
/**
* 安全模式这里指定token通过Authorization头请求头传递
*/
private List<SecurityScheme> securitySchemes()
{
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
return apiKeyList;
}
/**
* 安全上下文
*/
private List<SecurityContext> securityContexts()
{
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}
/**
* 默认的安全上引用
*/
private List<SecurityReference> defaultAuth()
{
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("TitleVetti Dashboard API Documentation")
// 描述
.description("Description: Explanation of all interface structures in the project")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本
.version("Version:" + ruoyiConfig.getVersion())
.build();
}
}

View File

@@ -0,0 +1,20 @@
package com.vetti.web.service;
import java.util.Properties;
/**
* 国际化操作 信息 服务类
*
* @author WangXiangShun
* @since 2025-08-27
*/
public interface ISystemI18nGainService {
/**
* 获取国际化数据
*
* @return
*/
Properties getI18nInfo();
}

View File

@@ -0,0 +1,48 @@
package com.vetti.web.service.impl;
import cn.hutool.core.util.StrUtil;
import com.vetti.common.config.InternationalProperties;
import com.vetti.common.exception.ServiceException;
import com.vetti.common.utils.I18nUtil;
import com.vetti.common.utils.MessageUtils;
import com.vetti.common.utils.spring.SpringUtils;
import com.vetti.web.service.ISystemI18nGainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Properties;
/**
* 国际化操作 信息 服务实现类
*
* @author WangXiangShun
* @since 2025-08-27
*/
@Slf4j
@Service
public class SystemI18nGainServiceImpl implements ISystemI18nGainService {
@Autowired
protected HttpServletRequest request;
/**
* 获取国际化数据
*
* @return
*/
@Override
public Properties getI18nInfo() {
//获取语言类型
InternationalProperties iProperties = SpringUtils.getBean(InternationalProperties.class);
// 获取客户语言环境
String lang = iProperties.getLang();
if(StrUtil.isEmpty(lang)){
throw new ServiceException(MessageUtils.messageCustomize("systemExceptionSystemI18nGainServiceImpl10001"));
}
Properties properties = I18nUtil.loadI18nProp(lang);
return properties;
}
}

View File

@@ -0,0 +1 @@
restart.include.json=/com.alibaba.fastjson2.*.jar

View File

@@ -0,0 +1,153 @@
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数默认为100
accept-count: 1000
threads:
# tomcat最大线程数默认为200
max: 800
# Tomcat启动初始化的线程数默认值10
min-spare: 100
# 日志配置
logging:
level:
com.vetti: debug
org.springframework: warn
pathUrl: ./logs/
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://test.hotake.cn:13306/htk-vetti?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: Hamkke@2021
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# redis 配置
redis:
# 地址
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password: Hamkke@2021
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
fs:
minio:
endpoint: http://test.hotake.cn:19000 # MinIO 服务地址
access-key: minioadmin # 访问密钥(替换为你的 Access Key
secret-key: minioadmin # 密钥(替换为你的 Secret Key
max-file-size: 104857600 #字节Byte
bucket-name.: # 存储桶名称(提前在 MinIO 控制台创建)
system-fs: system-fs
# Twilio SendGrid 配置
twilio:
sendgrid:
#你的SendGrid API Key
api-key: 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
#已验证的发送邮箱地址
from-email: noreply@routez.app
#你的应用名称
from-name: RouteZ
template-ids:
routez-verification-code: d-321fee8a85704983849eb1f69313ae24
verification:
code:
email:
# 验证码长度
length: 5
# 验证码过期时间(分钟)
expiration-minutes: 10
here-map:
api-key: q1gsOSZ899P8dVaoW2HZXq40W-AqCwB6iON5tw7-sqI
geocoding-api-url: https://geocode.search.hereapi.com/v1/geocode
reverse-geocoding-api-url: https://revgeocode.search.hereapi.com/v1/revgeocode
router-api-url: https://router.hereapi.com/v8/routes
http:
client:
connect-timeout-seconds: 10

View File

@@ -0,0 +1,94 @@
# 项目相关配置
vetti:
# 名称
name: Vetti
# 版本
version: 3.9.0
# 版权年份
copyrightYear: 2025
# 文件路径 示例( Windows配置D:/vetti/uploadPathLinux配置 /home/vetti/uploadPath
profile: ./uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
# 国际化默认语言环境
international:
enable: true
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: druid
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期默认30分钟
expireTime: 30
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.vetti.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
#################################### Swagger start #################################
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /prod-api
#################################### Swagger end ###################################
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# 安全配置
security:
# 不校验白名单
ignore:
whites:
- /v1/app/system/i18nGain/getInfo
- /v1/app/verificationEmail/send
- /v1/app/verificationEmail/verify
- /v1/app/auth/appLogin
- /v1/app/auth/appRegister
- /v1/app/auth/appResetPassword
- /v1/app/auth/getUserInfoByEmail
- /v1/app/verificationEmail/register/send

View File

@@ -0,0 +1,2 @@
Application Version: ${vetti.version}
Spring Boot Version: ${spring-boot.version}

View File

@@ -0,0 +1,39 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{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}]

View File

@@ -0,0 +1,46 @@
#异常消息
# system.exception.类名.编号=* Required
systemCommonTip10001 = Operation Successful
systemExceptionSystemI18nGainServiceImpl10001 = The language type does not exist. Please try again later
systemExceptionTokenController10001 = Illegal login information
systemExceptionTokenController10002 = Registered Successfully
systemExceptionTokenController10003 = Password Reset Successful
systemExceptionSysAppLoginServiceImpl10001 = Email Cannot Be Empty
systemExceptionSysAppLoginServiceImpl10002 = Account Or Password Error
systemExceptionSysAppLoginServiceImpl10003 = Account Or Password Error
systemExceptionSysAppLoginServiceImpl10004 = Email Already In Use
systemExceptionSysAppLoginServiceImpl10005 = Invalid Verification Code
systemExceptionSysAppLoginServiceImpl10006 = Email Does Not Exist
systemExceptionSysAppLoginServiceImpl10007 = Passwords Do Not Match
systemExceptionSysAppLoginServiceImpl10008 = The New PassWord Cannot Match The Old PassWord
systemExceptionSysAppLoginServiceImpl10009 = The Current Logged In User Is Abnormal. Please Log In Again Or Contact Management
systemExceptionSysAppLoginServiceImpl10010 = Token has expired
systemExceptionSysAppLoginServiceImpl10011 = Invalid issuer
systemExceptionSysAppLoginServiceImpl10012 = Invalid audience
systemExceptionSysAppLoginServiceImpl10013 = Invalid token signature
systemExceptionSysAppLoginServiceImpl10014 = Failed to verify token
systemExceptionSysAppLoginServiceImpl10015 = Email unverified
systemExceptionSysAppLoginServiceImpl10016 = Invalid Google ID token
systemExceptionAuthFilter10001 = Token Cannot Be Empty
systemExceptionAuthFilter10002 = Token Has Expired Or Verification Is Incorrect
systemExceptionAuthFilter10003 = Login Status Has Expired
systemExceptionAuthFilter10004 = Token Verification Failed
systemVerificationEmailController10001 = The Verification Code Has Been Sent To Your Email
systemVerificationEmailController10002 = Failed To Send Verification Code, Please Try Again Later
systemVerificationEmailController10003 = Verification Code Verification Successful
systemVerificationEmailController10004 = The Verification Code Is Invalid Or Has Expired
SystemCommandOverhaulAppController10001 = Operation Successful
systemEmailUtil10001 = Sending Email Failed
systemR10001 = Operation Successful
#管理端
# manager.页面,字段 = User Manager
VerificationEmailTiTle = Your verification code
VerificationEmailContent = Your verification code is: {0}, valid for {1} minutes.

View File

@@ -0,0 +1,47 @@
#异常消息
# system.exception.类名.编号=* 必须填写
systemCommonTip10001 = 操作成功
systemExceptionSystemI18nGainServiceImpl10001 = 语言类型不存在,请稍后再试
systemExceptionTokenController10001 = 非法登录信息
systemExceptionTokenController10002 = 注册成功
systemExceptionTokenController10003 = 密码重置成功
systemExceptionSysAppLoginServiceImpl10001 = 邮箱不可为空
systemExceptionSysAppLoginServiceImpl10002 = 账号或者密码错误
systemExceptionSysAppLoginServiceImpl10003 = 账号或者密码错误
systemExceptionSysAppLoginServiceImpl10004 = 邮箱已被使用
systemExceptionSysAppLoginServiceImpl10005 = 验证码无效
systemExceptionSysAppLoginServiceImpl10006 = 邮箱不存在
systemExceptionSysAppLoginServiceImpl10007 = 密码不一致
systemExceptionSysAppLoginServiceImpl10008 = 新密码不能与旧密码一致
systemExceptionSysAppLoginServiceImpl10009 = 当前登录用户异常,请重新登录或联系管理
systemExceptionSysAppLoginServiceImpl10010 = 令牌已过期
systemExceptionSysAppLoginServiceImpl10011 = 颁发者无效
systemExceptionSysAppLoginServiceImpl10012 = 无效访问群体
systemExceptionSysAppLoginServiceImpl10013 = 令牌签名无效
systemExceptionSysAppLoginServiceImpl10014 = 验证令牌失败
systemExceptionSysAppLoginServiceImpl10015 = 电子邮件未验证
systemExceptionSysAppLoginServiceImpl10016 = 无效的Google ID令牌
systemExceptionAuthFilter10001 = 令牌不能为空
systemExceptionAuthFilter10002 = 令牌已过期或验证不正确
systemExceptionAuthFilter10003 = 登录状态已过期
systemExceptionAuthFilter10004 = 令牌验证失败
systemVerificationEmailController10001 = 验证码已发送到你的邮箱
systemVerificationEmailController10002 = 发送验证码失败,请稍后重试
systemVerificationEmailController10003 = 验证码验证成功
systemVerificationEmailController10004 = 验证码无效或已过期
SystemCommandOverhaulAppController10001 = 操作成功
systemEmailUtil10001 = 发送邮件失败
systemR10001 = 操作成功
#管理端
# manager.页面,字段 = 用户管理
VerificationEmailTiTle = 你的验证码
VerificationEmailContent = 你的验证码是: {0},有效期为 {1} 分钟。

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 读取yml配置文件中的路径地址-->
<springProperty scope="context" name="logPath" source="logging.pathUrl" defaultValue="logDir"/>
<!-- 日志存放路径 -->
<property name="log.path" value="${logPath}" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.vetti" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
</settings>
</configuration>

View File

@@ -0,0 +1 @@
restart.include.json=/com.alibaba.fastjson2.*.jar

View File

@@ -0,0 +1,153 @@
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数默认为100
accept-count: 1000
threads:
# tomcat最大线程数默认为200
max: 800
# Tomcat启动初始化的线程数默认值10
min-spare: 100
# 日志配置
logging:
level:
com.vetti: debug
org.springframework: warn
pathUrl: ./logs/
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://test.hotake.cn:13306/htk-vetti?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: Hamkke@2021
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# redis 配置
redis:
# 地址
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password: Hamkke@2021
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
fs:
minio:
endpoint: http://test.hotake.cn:19000 # MinIO 服务地址
access-key: minioadmin # 访问密钥(替换为你的 Access Key
secret-key: minioadmin # 密钥(替换为你的 Secret Key
max-file-size: 104857600 #字节Byte
bucket-name.: # 存储桶名称(提前在 MinIO 控制台创建)
system-fs: system-fs
# Twilio SendGrid 配置
twilio:
sendgrid:
#你的SendGrid API Key
api-key: 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
#已验证的发送邮箱地址
from-email: noreply@routez.app
#你的应用名称
from-name: RouteZ
template-ids:
routez-verification-code: d-321fee8a85704983849eb1f69313ae24
verification:
code:
email:
# 验证码长度
length: 5
# 验证码过期时间(分钟)
expiration-minutes: 10
here-map:
api-key: q1gsOSZ899P8dVaoW2HZXq40W-AqCwB6iON5tw7-sqI
geocoding-api-url: https://geocode.search.hereapi.com/v1/geocode
reverse-geocoding-api-url: https://revgeocode.search.hereapi.com/v1/revgeocode
router-api-url: https://router.hereapi.com/v8/routes
http:
client:
connect-timeout-seconds: 10

View File

@@ -0,0 +1,94 @@
# 项目相关配置
vetti:
# 名称
name: Vetti
# 版本
version: 3.9.0
# 版权年份
copyrightYear: 2025
# 文件路径 示例( Windows配置D:/vetti/uploadPathLinux配置 /home/vetti/uploadPath
profile: ./uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
# 国际化默认语言环境
international:
enable: true
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: druid
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期默认30分钟
expireTime: 30
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.vetti.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
#################################### Swagger start #################################
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /prod-api
#################################### Swagger end ###################################
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# 安全配置
security:
# 不校验白名单
ignore:
whites:
- /v1/app/system/i18nGain/getInfo
- /v1/app/verificationEmail/send
- /v1/app/verificationEmail/verify
- /v1/app/auth/appLogin
- /v1/app/auth/appRegister
- /v1/app/auth/appResetPassword
- /v1/app/auth/getUserInfoByEmail
- /v1/app/verificationEmail/register/send

View File

@@ -0,0 +1,2 @@
Application Version: ${vetti.version}
Spring Boot Version: ${spring-boot.version}

View File

@@ -0,0 +1,39 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{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}]

View File

@@ -0,0 +1,496 @@
#异常消息
# system.exception.类名.编号=* Required
systemCommonTip10001 = Operation Successful
systemExceptionSystemI18nGainServiceImpl10001 = The language type does not exist. Please try again later
systemExceptionTokenController10001 = Illegal login information
systemExceptionTokenController10002 = Registered Successfully
systemExceptionTokenController10003 = Password Reset Successful
systemExceptionSysAppLoginServiceImpl10001 = Email Cannot Be Empty
systemExceptionSysAppLoginServiceImpl10002 = Account Or Password Error
systemExceptionSysAppLoginServiceImpl10003 = Account Or Password Error
systemExceptionSysAppLoginServiceImpl10004 = Email Already In Use
systemExceptionSysAppLoginServiceImpl10005 = Invalid Verification Code
systemExceptionSysAppLoginServiceImpl10006 = Email Does Not Exist
systemExceptionSysAppLoginServiceImpl10007 = Passwords Do Not Match
systemExceptionSysAppLoginServiceImpl10008 = The New PassWord Cannot Match The Old PassWord
systemExceptionSysAppLoginServiceImpl10009 = The Current Logged In User Is Abnormal. Please Log In Again Or Contact Management
systemExceptionSysAppLoginServiceImpl10010 = Token has expired
systemExceptionSysAppLoginServiceImpl10011 = Invalid issuer
systemExceptionSysAppLoginServiceImpl10012 = Invalid audience
systemExceptionSysAppLoginServiceImpl10013 = Invalid token signature
systemExceptionSysAppLoginServiceImpl10014 = Failed to verify token
systemExceptionSysAppLoginServiceImpl10015 = Email unverified
systemExceptionSysAppLoginServiceImpl10016 = Invalid Google ID token
systemExceptionAuthFilter10001 = Token Cannot Be Empty
systemExceptionAuthFilter10002 = Token Has Expired Or Verification Is Incorrect
systemExceptionAuthFilter10003 = Login Status Has Expired
systemExceptionAuthFilter10004 = Token Verification Failed
systemVerificationEmailController10001 = The Verification Code Has Been Sent To Your Email
systemVerificationEmailController10002 = Failed To Send Verification Code, Please Try Again Later
systemVerificationEmailController10003 = Verification Code Verification Successful
systemVerificationEmailController10004 = The Verification Code Is Invalid Or Has Expired
SystemCommandOverhaulAppController10001 = Operation Successful
systemEmailUtil10001 = Sending Email Failed
systemR10001 = Operation Successful
#管理端
# manager.页面,字段 = User Manager
VerificationEmailTiTle = Your verification code
VerificationEmailContent = Your verification code is: {0}, valid for {1} minutes.
#APP端
AppSystemPassWordTip10001 = Two Inputs Are Inconsistent
AppSystemPassWordTip10002 = Cannot Include Your Name Or Email
AppSystemPassWordTip10003 = Contains At Least 8 Characters
AppSystemPassWordTip10004 = Contains Symbols Or Numbers
OnboardingOnboarding2ActionSkip = Skip
OnboardingOnboarding2TitleOne = Smart, fast routes every time.
OnboardingOnboarding2InfoOne = Optimised Navigation to get you there faster and safer.
OnboardingOnboarding2ButtonGetStarted = Get Started
OnboardingOnboarding3ActionSkip = Skip
OnboardingOnboarding3TitleOne = Stay compliant, drive easy.
OnboardingOnboarding3InfoOne = Seamless EWD & CoR compliance to keep you on track.
OnboardingOnboarding3ButtonGetStarted = Get Started
OnboardingGetStartedTitleOne = Maximise earnings, minimise hassle.
OnboardingGetStartedInfoOne = Sign up or login to see what happening near you
OnboardingGetStartedButtonEamil = Continue with Email
OnboardingGetStartedButtonGoogle = Continue with Google
OnboardingGetStartedButtonApple = Continue with Apple
AuthenticationLoginEmptyStateTitleOne = Sign in to Routez
AuthenticationLoginEmptyStateInfoOne = Welcome back! Please enter yout details.
AuthenticationLoginEmptyStatePlaceholderEamil = Email
AuthenticationLoginEmptyStatePlaceholderPassword = Password
AuthenticationLoginEmptyStateInfoTwo = Forgot password?
AuthenticationLoginEmptyStateActionReset = Reset it
AuthenticationLoginEmptyStateButtonSignin = Sign in
AuthenticationLoginEmptyStateButtonGoogle = Sign in with Google
AuthenticationLoginEmptyStateButtonApple = Sign in with Apple
AuthenticationLoginEmptyStateInfoThree = Dont have and account?
AuthenticationLoginEmptyStateActionSignup = Sign Up
AuthenticationSignUpEmptyStateTitleOne = Sign Up
AuthenticationSignUpEmptyStatePlaceholderFullname = Fullname
AuthenticationSignUpEmptyStatePlaceholderEamil = Email
AuthenticationSignUpEmptyStatePlaceholderPassword = Password
AuthenticationSignUpEmptyStatePlaceholderFullnameTips = Please Check Fullname
AuthenticationSignUpEmptyStatePlaceholderEamilTips = Please Check Email
AuthenticationSignUpEmptyStatePlaceholderPasswordTips = Please Check Password
AuthenticationSignUpEmptyStateInfoOne = By signing up, you agree to our
AuthenticationSignUpEmptyStateLegalService = Terms of Service
AuthenticationSignUpEmptyStateInfoTwo = and
AuthenticationSignUpEmptyStateLegalpolicy = Privacy Policy.
AuthenticationSignUpEmptyStateButtonSignup = Sign Up
AuthenticationSignUpEmptyStateButtonGoogle = Sign in with Google
AuthenticationSignUpEmptyStateButtonApple = Sign in with Apple
AuthenticationSignUpEmptyStateInfoThree = Already have and account?
AuthenticationSignUpEmptyStateActionSignin = Sign in
AuthenticationEnterVerificationCodeTitleOne = Almost there!
AuthenticationEnterVerificationCodeInfoOne = Check your email inbox and input the verification code to verify your account.
AuthenticationEnterVerificationCodeButtonContinue = Continue
AuthenticationEnterVerificationCodeButtonResend = Resend Code
AuthenticationForgotPasswordTitleOne = Cant sign in?
AuthenticationForgotPasswordInfoOne = Enter the email associated with your account, and Carline will send you a link to reset your password.
AuthenticationForgotPasswordPlaceholderEamil = Email
AuthenticationForgotPasswordButtonReset = Reset Password
AuthenticationForgotPasswordButtonReturn = Return to Sign In
AuthenticationEnterNewPasswordTitleOne = Create your new password
AuthenticationEnterNewPasswordInfoOne = Your new password must be different from previous password.
AuthenticationEnterNewPasswordPlaceholderPassword = Password
AuthenticationEnterNewPasswordPlaceholderRepeat = Password
AuthenticationEnterNewPasswordAlertOne = Must not contain your name or email
AuthenticationEnterNewPasswordAlertTwo = At least 8 characters
AuthenticationEnterNewPasswordAlertThree = Contains a symbol or a number
AuthenticationEnterNewPasswordButtonCreate = Create New Password
DriverMyProfileTitleOne = Profile
DriverMyProfileButtonMyProfile = My Profile
DriverMyProfileTitleTwo = General
DriverMyProfileActionOne = Appointment
DriverMyProfileActionTwo = Test Drive
DriverMyProfileActionThree = My vouchers
DriverMyProfileTitleThree = Browse history
DriverMyProfileNavMome = Home
DriverMyProfileNavFavourite = Map
DriverMyProfileNavMessage = Fleet
DriverMyProfileNavProfile = Profile
DriverPreTripCheckTitleOne = Pre-Trip Check
DriverPreTripCheckTitleTwo = Next Service
DriverPreTripCheckButtonStart = Start Check
DriverPreTripCheckButtonOne = Check DOne
DriverPreTripCheckButtonFinish = Finish Check
DriverPreTripCheckButtonStartfull = Start Full Inspection
DriverPreTripCheckListTitleOne = Pre-Trip Inspection
DriverPreTripCheckListInfoOne = Complete
DriverPreTripCheckDetailInfoOne = Inspection Point
DriverPreTripCheckDetailTitleOne = Required Specifications
DriverPreTripCheckDetailButtonFail = Fail
DriverPreTripCheckDetailButtonPass = Pass
DriverPreTripCheckDetailLabelOne = Issue Details
DriverPreTripCheckDetailTitleTwo = Photos
DriverPreTripCheckDetailButtonAdd = Add Photo
DriverPreTripCheckDetailButtonSubmit = Submit
DriverPreTripCheckHistoryTitleOne = Hours of Service
DriverPreTripCheckHistoryInfoOne = Last Break
DriverPreTripCheckHistoryInfoTwo = Next Required
DriverPreTripCheckHistoryTitleTwo = Current Route
DriverPreTripCheckHistoryTitleThree = Current Vehicle
DriverPreTripCheckHistoryButtonStartInspection = Start Inspection
DriverPreTripCheckHistoryButtonViewDetails = View Details
DriverPreTripCheckHistoryTitleFour = Recent Inspections
DriverPreTripCheckHistoryStatusIssues = issues found
DriverPreTripCheckHistoryButtonView = View
DriverJobCenterTitleOne = Truck
DriverJobCenterStatusCompleted = Completed
DriverJobCenterStatusReamining = Remaining
DriverJobCenterTitleTwo = Next Job
DriverJobCenterInfoPackages = packages
DriverJobCenterButtonDeliverTo = Deliver to Main Dock
DriverJobCenterTitleThree = Upcoming Stops
DriverJobCenterButtonSelect = Select Job
DriverCurrentDeliveryTitleOne = Current Delivery
DriverCurrentDeliveryTitleTwo = Packages
DriverCurrentDeliveryTitleThree = Delivery Notes
DriverCurrentDeliveryButtonStartNav = Start Navigation
DriverCurrentDeliveryCompleteTitleOne = Current Delivery
DriverCurrentDeliveryCompleteTitleTwo = Packages
DriverCurrentDeliveryCompleteTitleThree = Delivery Notes
DriverCurrentDeliveryCompleteButtonComplete = Complete Delivery
TruckTruckParameterTitleOne = Truck details
TruckTruckParameterTitleTwo = Load Capacity
TruckTruckParameterTitleThree = Condition
TruckTruckParameterButtonAdd = Add to Fleet
TruckVehicleProfileTitleOne = Add My Vehicles
TruckVehicleProfileInfoOne = The selected vehicles will be used for route calculation and guidance.
TruckVehicleProfileTitleTwo = Trucks
TruckVehicleProfileTitleThree = Passenger Cars
TruckVehicleProfileButtonAdd = Add Vehicle
# 新增加
NavigationReferenceNavHomePlaceholderSearch = Where to?
NavigationReferenceNavHomeButtonHome = home
NavigationReferenceNavHomeButtonDepo = depo
NavigationReferenceNavHomeButtonSaved = saved
NavigationReferenceNavDockUpPlaceholderSearch = Where to?
NavigationReferenceNavDockUpButtonHome = home
NavigationReferenceNavDockUpButtonDepo = depo
NavigationReferenceNavDockUpButtonAddPlace = add place
NavigationReferenceNavDockUpTitleSaved = Saved Location
NavigationReferenceNavDockUpActionSeeAll = See all
NavigationReferenceNavDockUpButtonAddJobLocation = Add Job Location
NavigationReferenceNavSearchNormalPlaceholderSearch = Where to?
NavigationReferenceNavSearchNormalActionCancel = Cancel
NavigationReferenceNavSearchNormalButtonRestaurant = Restaurant
NavigationReferenceNavSearchNormalButtonCoffee = Coffee
NavigationReferenceNavSearchNormalButtonATM = ATM
NavigationReferenceNavSearchNormalButtonShopping = Shopping
NavigationReferenceNavSearchNormalButtonFuel = Fuel
NavigationReferenceNavSearchNormalTitleRecentSearches = RECENT SEARCHES
NavigationReferenceNavSearchNormalActionClear = Clear
NavigationReferenceNavSearchSelectPlaceholderYourLocation = Your Location
NavigationReferenceNavSearchSelectButtonTruck = Truck
NavigationReferenceNavSearchSelectButtonVan = Van
NavigationReferenceNavSearchSelectButtonCar = Car
NavigationReferenceNavSearchSelectTitleLeavingNow = Leaving Now
NavigationReferenceNavSearchSelectActionRoutePreferences = Route Preferences
NavigationReferenceNavRoutePreferenceTitleRoutePreferences = Route Preferences
NavigationReferenceNavRoutePreferenceinfoCar = Car
NavigationReferenceNavRoutePreferenceToggleSetMaxVehicleSpeed = Set max vehicle speed
NavigationReferenceNavRoutePreferenceInfoOne = This speed will be used to calculate travel time and alert you in case of over speeding
NavigationReferenceNavRoutePreferenceTitleZoneRegulations = ZONE REGULATIONS
NavigationReferenceNavRoutePreferenceMenuLicensePlateNumber = License plate number
NavigationReferenceNavRoutePreferenceInfoTwo = Indonesia and the Philippines
NavigationReferenceNavRoutePreferenceTitleAVOID = AVOID
NavigationReferenceNavRoutePreferenceCheckboxAvoidUTurns = Avoid U-turns
NavigationReferenceNavRoutePreferenceCheckboxAvoidFerries = Avoid ferries
NavigationReferenceNavRoutePreferenceCheckboxAvoidHighways = Avoid highways
NavigationReferenceNavRoutePreferenceCheckboxAvoidTunnels = Avoid tunnels
NavigationReferenceNavRoutePreferenceCheckboxAvoidTollRoads = Avoid toll roads
NavigationReferenceNavRoutePreferenceCheckboxAvoidUnpavedRoads = Avoid unpaved roads
NavigationReferenceNavigatingTitleArrival = arrival
NavigationReferenceNavigatingTitleRemaining = remaining
NavigationReferenceNavigatingTitleDistance = distance
NavigationReferenceNavOverviewTitleArrival = arrival
NavigationReferenceNavOverviewTitleAddAStop = ADD A STOP
NavigationReferenceNavSearchSelectButtonSearch = Search
NavigationReferenceNavSearchSelectButtonGas = Gas
NavigationReferenceNavSearchSelectButtonParking = Parking
NavigationReferenceNavSearchSelectButtonRestaurants = Restaurants
NavigationReferenceNavOverviewTitleQuickSettings = QUICK SETTINGS
NavigationReferenceNavOverviewToggleSpeedAlert = Speed alert
NavigationReferenceNavOverviewToggleOffline = Offline
NavigationReferenceNavOverviewToggleTheme = Theme
NavigationReferenceNavOverviewButtonMoreSetting = More setting
NavigationReferenceNavOverviewTitleOverview = OVERVIEW
NavigationReferenceHomePageMenuStartDailyRoute = Start Daily Route
NavigationReferenceHomePageMenuCurrentRoute = Current Route
NavigationReferenceHomePageInfoNoActiveRoute = No active route
NavigationReferenceHomePageMenuMyDiary = My Diary / Log
NavigationReferenceHomePageInfoElectronicWorkDiary = Electronic work diary
NavigationReferenceHomePageMenuTripsReports = Trips & Reports
NavigationReferenceHomePageInfoViewTripHistory = View trip history
NavigationReferenceHomePageMenuDefectsMaintenance = Defects & Maintenance
NavigationReferenceHomePageInfoVehicleIssues = Vehicle issues
NavigationReferenceSetVehicleInfoTitleDimensionsWeight = Dimensions & Weight
NavigationReferenceSetVehicleInfoTitleVehicleSpecifications = Vehicle Specifications
NavigationReferenceSetVehicleInfoinfoOne = Enter total size and weight including trailersand load
NavigationReferenceSetVehicleInfoTitlePhysicalDimensions = Physical Dimensions
NavigationReferenceSetVehicleInfoTitleUnitM = m
NavigationReferenceSetVehicleInfoTitleUnitCM = cm
NavigationReferenceSetVehicleInfoTitleHeight = Height
NavigationReferenceSetVehicleInfoTitleWidth = Width
NavigationReferenceSetVehicleInfoTitleLength = Length
NavigationReferenceSetVehicleInfoTitleWeightSpecifications = Weight Specifications
NavigationReferenceSetVehicleInfoTitleTotalWeight = Total Weight
NavigationReferenceSetVehicleInfoTitleUnitTonnes = tonnes
NavigationReferenceSetVehicleInfoInfoTwo = 15 metric tons =33,069 lbs
NavigationReferenceSetVehicleInfoButtonNext = Next: Hazardous Materials
NavigationReferenceHazardousMaterialsTitleHazardousMaterials = Hazardous Materials
NavigationReferenceHazardousMaterialsTitleCargoClassification = Cargo Classification
NavigationReferenceHazardousMaterialsInfoOne = Select any hazardous materials you will betransporting
NavigationReferenceHazardousMaterialsTitleHazmatCategories = Hazmat Categories
NavigationReferenceHazardousMaterialsToggleExplosives = Explosives
NavigationReferenceHazardousMaterialsToggleGas = Gas
NavigationReferenceHazardousMaterialsToggleFlammable = Flammable
NavigationReferenceHazardousMaterialsTogglePoison = Poison
NavigationReferenceHazardousMaterialsToggleRadioactive = Radioactive
NavigationReferenceHazardousMaterialsToggleCorrosive = Corrosive
NavigationReferenceHazardousMaterialsToggleMiscellaneous = Miscellaneous
NavigationReferenceHazardousMaterialsButtonNext = Next: Tunnel Category
NavigationReferenceTripSummaryTitleTripSummary = Trip Summary
NavigationReferenceTripSummaryTitleTripCompletedSuccessfully = Trip Completed Successfully
NavigationReferenceTripSummaryInfoOne = All compliance requirements met
NavigationReferenceTripSummaryTitleTripStatistics = Trip Statistics
NavigationReferenceTripSummaryTitleStartTime = START TIME
NavigationReferenceTripSummaryTitleEndTime = END TIME
NavigationReferenceTripSummaryTitleDistance = DISTANCE
NavigationReferenceTripSummaryTitleDuration = DURATION
NavigationReferenceTripSummaryTitleComplianceStatus = Compliance Status
NavigationReferenceTripSummaryStatusOne = Pre-trip inspection completed
NavigationReferenceTripSummaryStatusTwo = Route configured properly
NavigationReferenceTripSummaryStatusThree = Break requirements met
NavigationReferenceTripSummaryStatusFour = No incidents reported
NavigationReferenceTripSummaryButtonPDF = PDF
NavigationReferenceTripSummaryButtonDone = Done
TripReportAndProfileDefectsMaintenanceButtonBack = Back
TripReportAndProfileDefectsMaintenanceTitleDefectsMaintenance = Defects & Maintenance
TripReportAndProfileDefectsMaintenanceTabOpen = Open
TripReportAndProfileDefectsMaintenanceTabResolved = Resolved
TripReportAndProfileDefectsMaintenanceTitleReported = Reported
TripReportAndProfileDefectsMaintenanceTitleLoaction = Loaction
TripReportAndProfileDefectsMaintenanceTitleReporter = Reporter
TripReportAndProfileDefectsMaintenanceButtonAddNewDefect = Add New Defect
TripReportAndProfileMyDiaryLogButtonBack = Back
TripReportAndProfileMyDiaryLogTitleMyDiaryLog = My Diary / Log
TripReportAndProfileMyDiaryLogTabToday = Today
TripReportAndProfileMyDiaryLogTabWeek = Week
TripReportAndProfileMyDiaryLogTab28Days = 28Days
TripReportAndProfileMyDiaryLogTitleDailyTotals = Daily Totals
TripReportAndProfileMyDiaryLogTitleDrivingTime = Driving Time
TripReportAndProfileMyDiaryLogTitleOnDutyTime = On Duty Time
TripReportAndProfileMyDiaryLogTitleOffDutyTime = Off Duty Time
TripReportAndProfileMyDiaryLogTitleTotalBreaks = Total Breaks
TripReportAndProfileMyDiaryLogStatusRestDay = Rest Day
TripReportAndProfileMyDiaryLogTitleOffDuty = Off Duty
TripReportAndProfileMyDiaryLogTitleDriving = Driving
TripReportAndProfileMyDiaryLogTitleOnDuty = OnDuty
TripReportAndProfileMyDiaryLogStatusCompliant = Compliant
TripReportAndProfileMyDiaryLogStatusViolation = Violation
TripReportAndProfileMyDiaryLogTitle28DayComplianceOverview = 28-Day Compliance Overview
TripReportAndProfileMyDiaryLogStatusOff = Off
TripReportAndProfileMyDiaryLogButtonExport = Export
TripReportAndProfileMyDiaryLogButtonEmailReport = Email Report
TripReportAndProfileTripsReportsButtonBack = Back
TripReportAndProfileTripsReportsTitleTripsReports = Trips & Reports
TripReportAndProfileTripsReportsTabRecent = Recent
TripReportAndProfileTripsReportsTabThisWeek = This Week
TripReportAndProfileTripsReportsTabThisMonth = This Month
TripReportAndProfileTripsReportsTitleTotalTrips = TOTAL TRIPS
TripReportAndProfileTripsReportsTitleCompliance = COMPLIANCE
TripReportAndProfileTripsReportsTitleDistance = Distance
TripReportAndProfileTripsReportsTitleDuration = Duration
TripReportAndProfileTripsReportsTitleLoadType = Load Type
TripReportAndProfileTripsReportsActionViewDetails = View Details
TripReportAndProfileTripsReportsButtonExportAl = Export Al
TripReportAndProfileTripsReportsButtonFilter = Filter
TripReportAndProfileMyProfileDetailButtonBack = Back
TripReportAndProfileMyProfileDetailTitleProfile = Profile
TripReportAndProfileMyProfileDetailActionEdit = Edit
TripReportAndProfileMyProfileDetailTitleDriverInformation = Driver Information
TripReportAndProfileMyProfileDetailTitleLicenceNumber = Licence Number
TripReportAndProfileMyProfileDetailTitleState = State
TripReportAndProfileMyProfileDetailTitleExpiryDate = Expiry Date
TripReportAndProfileMyProfileDetailTitleABN = ABN
TripReportAndProfileMyProfileDetailTitleMyVehicles = My Vehicles
TripReportAndProfileMyProfileDetailTitleAccountSettings = Account Settings
TripReportAndProfileMyProfileDetailTitlePushNotifications = Push Notifications
TripReportAndProfileMyProfileDetailTitleLocationServices = Location Services
TripReportAndProfileMyProfileDetailTitleAutoBackup = Auto Backup
TripReportAndProfileMyProfileDetailButtonSettings = Settings
TripReportAndProfileMyProfileDetailButtonSign Out = Sign Out
TripReportAndProfileTripInProgressTitleTripInProgress = Trip in Progress
TripReportAndProfileTripInProgressActionActive = Active
TripReportAndProfileTripInProgressTitleCurrentLocation = Current Location
TripReportAndProfileTripInProgressTitleDestination = Destination
TripReportAndProfileTripInProgressTitleTripDuration = Trip Duration
TripReportAndProfileTripInProgressTitleWorkTime = Work Time
TripReportAndProfileTripInProgressTitleDriveTime = Drive Time
TripReportAndProfileTripInProgressTitleRestTaken = Rest Taken
TripReportAndProfileTripInProgressTitleQuickActions = Quick Actions
TripReportAndProfileTripInProgressButtonTakeBreak = Take Break
TripReportAndProfileTripInProgressButtonReportlssue = Report lssue
TripReportAndProfileTripInProgressButtonFuelStop = Fuel Stop
TripReportAndProfileTripInProgressButtonAddNote = Add Note
TripReportAndProfileTripInProgressButtonEndTrip = End Trip
TripReportAndProfileEndTripButtonBack = Back
TripReportAndProfileEndTripTitleEndTrip = End Trip
TripReportAndProfileEndTripTitleTripSummary = Trip Summary
TripReportAndProfileEndTripTitleStartLocation = Start Location
TripReportAndProfileEndTripTitleEndLocation = End Location
TripReportAndProfileEndTripTitleTotalDistance = Total Distance
TripReportAndProfileEndTripTitleTripDuration = Trip Duration
TripReportAndProfileEndTripTitleFuelUsed = Fuel Used (Est.)
TripReportAndProfileEndTripTitleFinalOdometerLu = Final OdometerLu
TripReportAndProfileEndTripTitleStartOdometer = Start Odometer
TripReportAndProfileEndTripTitleEndOdometer = End Odometer
TripReportAndProfileEndTripTitleDeliveryConfirmation = Delivery Confirmation
TripReportAndProfileEndTripTitleCargoDeliveredSuccessfully = Cargo delivered successfully
TripReportAndProfileEndTripTitleDeliveryDocumentationComplete = Delivery documentation complete
TripReportAndProfileEndTripTitleVehicleParkedSafely = Vehicle parked safely
TripReportAndProfileEndTripPlaceholderAddTripNotes = Add trip notes (optional)
TripReportAndProfileEndTripButtonCompleteTrip = Complete Trip
TripReportAndProfileBreakRestButtonBack = Back
TripReportAndProfileBreakRestTitleBreakRest = Break & Rest
TripReportAndProfileBreakRestTitleBreakTimer = Break Timer
TripReportAndProfileBreakRestInfoOne = Minimum
TripReportAndProfileBreakRestInfoTwo = required
TripReportAndProfileBreakRestTitleWorkTimeSummary = Work Time Summary
TripReportAndProfileBreakRestTitleTotalWorkTime = Total Work Time
TripReportAndProfileBreakRestTitleContinuousDriving = Continuous Driving
TripReportAndProfileBreakRestTitleLastBreak = Last Break
TripReportAndProfileBreakRestTitleTotalBreaksToday = Total Breaks Today
TripReportAndProfileBreakRestTitleComplianceStatus = Compliance Status
TripReportAndProfileBreakRestTitleDailyWorkLimit = Daily Work Limit
TripReportAndProfileBreakRestTitleWeeklyHours = Weekly Hours
TripReportAndProfileBreakRestButtonEndBreakResume = End Break & Resume
TripReportAndProfileTripCompleteTitleTripComplete = Trip Complete!
TripReportAndProfileTripCompleteInfoOne = Your trip has been successfully logged and saved to your electronic work diary.
TripReportAndProfileTripCompleteTitleTripStatistics = Trip Statistics
TripReportAndProfileTripCompleteTitleDistance = Distance
TripReportAndProfileTripCompleteTitleDuration = Duration
TripReportAndProfileTripCompleteTitleAvgSpeed = Avg Speed
TripReportAndProfileTripCompleteTitleFuelUsed = Fuel Used
TripReportAndProfileTripCompleteTitleAllComplianceMet = All Compliance Met
TripReportAndProfileTripCompleteInfoTwo = Your trip complies with all NHVR regulations
TripReportAndProfileTripCompleteButtonViewSummary = View Summary
TripReportAndProfileTripCompleteButtonBackToDashboard = Back to Dashboard
TripReportAndProfileReportIncidentButtonBack = Back
TripReportAndProfileReportIncidentTitleReportIncident = Report Incident
TripReportAndProfileReportIncidentStatusUrgent = URGENT
TripReportAndProfileReportIncidentTitleIncidentType = Incident Type
TripReportAndProfileReportIncidentTitleLocationDetails = Location Details
TripReportAndProfileReportIncidentTitleCurrentLocation = Current Location
TripReportAndProfileReportIncidentPlaceholderOne = Nearest landmark or address
TripReportAndProfileReportIncidentTitleIncidentDetails = Incident Details
TripReportAndProfileReportIncidentPlaceholderTwo = Describe what happened..
TripReportAndProfileReportIncidentTitleAnyInjuries = Any injuries?
TripReportAndProfileReportIncidentButtonYes = Yes
TripReportAndProfileReportIncidentButtonNo = No
TripReportAndProfileReportIncidentTitleEmergencyServicesCalled = Emergency services called?
TripReportAndProfileReportIncidentTitlePhotosEvidence = Photos & Evidence
TripReportAndProfileReportIncidentButtonSubmitReport = Submit Report
TripReportAndProfileReportIncidentButtonSaveDraft = Save Draft
TripReportAndProfileDailySummaryButtonBack = Back
TripReportAndProfileDailySummaryTitleDailySummary = Daily Summary
TripReportAndProfileDailySummaryInfoOne = Work Day Complete
TripReportAndProfileDailySummaryTitleTodayPerformance = Today's Performance
TripReportAndProfileDailySummaryTitleTotalDistance = Total Distance
TripReportAndProfileDailySummaryTitleTotalWorkTime = Total work Time
TripReportAndProfileDailySummaryTitleTripsCompleted = Trips Completed
TripReportAndProfileDailySummaryTitleTotalBreaks = Total Breaks
TripReportAndProfileDailySummaryTitleTripDetails = Trip Details
TripReportAndProfileDailySummaryTitleTrip = Trip
TripReportAndProfileDailySummaryTitleComplianceSummary = Compliance Summary
TripReportAndProfileDailySummaryTitleWorkTimeLimits = Work Time Limits
TripReportAndProfileDailySummaryTitleRestBreaks = Rest Breaks
TripReportAndProfileDailySummaryTitleElectronicWorkDiary = Electronic Work Diary
TripReportAndProfileDailySummaryButtonExportReport = Export Report
TripReportAndProfileDailySummaryButtonSignOffDay = Sign Off Day
AuthenticationAddDriverTitleAddVehicle = Add Vehicle
AuthenticationAddDriverTitleVehicleInformation = Vehicle Information
AuthenticationAddDriverTagRegistration = REGISTRATION
AuthenticationAddDriverTagVIN = VIN(VEHICLEIDENTIFICATIONNUMBER)
AuthenticationAddDriverTagRegistrationExpiry = REGISTRATION EXPIRY
AuthenticationAddDriverTagInsuranceExpiry = INSURANCE EXPIRY
AuthenticationAddDriverTagVehicleType = VEHICLE TYPE
AuthenticationAddDriverOptionSelectType = Select type
AuthenticationAddDriverTagMake = MAKE
AuthenticationAddDriverTagModel = MODEL
AuthenticationAddDriverTagYear = YEAR
AuthenticationAddDriverTagColor = COLOR
AuthenticationAddDriverTagDimensionsCapacity = Dimensions & Capacity
AuthenticationAddDriverTagVehicleHeight = Vehicle Height
AuthenticationAddDriverTagVehicleWidth = Vehicle Width
AuthenticationAddDriverTagVehicleLength = Vehicle Length
AuthenticationAddDriverTagGrossWeight = Gross Weight
AuthenticationAddDriverTitleAdditionalSpecifications = Additional Specifications
AuthenticationAddDriverTagAxles = AXLES
AuthenticationAddDriverTagTrailers = TRAILERS
AuthenticationAddDriverTagWeightPerAxle = WEIGHT PER AXLE
AuthenticationAddDriverTagTruckType = TRUCK TYPE
AuthenticationAddDriverTitleHazardousMaterials = Hazardous Materials
AuthenticationAddDriverTagCarryingHazardousGoods = CARRYING HAZARDOUS GOODS?
AuthenticationAddDriverButtonNo = No
AuthenticationAddDriverButtonYes = Yes
AuthenticationAddDriverButtonAddVehicle = Add Vehicle
AuthenticationAddVehicleTitleDriverProfile = Driver Profile
AuthenticationAddVehicleTitlePersonalInformation = Personal Information
AuthenticationAddVehicleTagFirstName = FIRST NAME
AuthenticationAddVehicleTagLastName = LAST NAME
AuthenticationAddVehicleTagPhoneNumber = PHONE NUMBER
AuthenticationAddVehicleTitleLicenseCertification = License & Certification
AuthenticationAddVehicleTagDriverLicenseNumber = DRIVER'S LICENSE NUMBER
AuthenticationAddVehicleTagLicenseClass = LICENSE CLASS
AuthenticationAddVehicleTagLicenseState = LICENSE STATE
AuthenticationAddVehicleOptionSelect = Select
AuthenticationAddVehicleTagLicenseExpiry = LICENSE EXPIRY
AuthenticationAddVehicleTagMedicalCert = MEDICAL CERT
AuthenticationAddVehicleTitleHazmatCertification = Hazmat Certification
AuthenticationAddVehicleTagHazmatCertified = HAZMAT CERTIFIED?
AuthenticationAddVehicleButtonNo = No
AuthenticationAddVehicleButtonYes = Yes
AuthenticationAddVehicleButtonSave = Save
AuthenticationHazmatDialogTitleHazmatCertification = Hazmat Certification
AuthenticationHazmatDialogTagHazmatCertified = HAZMAT CERTIFIED?
AuthenticationHazmatDialogButtonNo = No
AuthenticationHazmatDialogButtonYes = Yes
AuthenticationHazmatDialogTagHazmatExpiryDate = HAZMAT EXPIRY DATE
AuthenticationHazmatDialogOptionSelect = Select date

View File

@@ -0,0 +1,498 @@
#异常消息
# system.exception.类名.编号=* 必须填写
systemCommonTip10001 = 操作成功
systemExceptionSystemI18nGainServiceImpl10001 = 语言类型不存在,请稍后再试
systemExceptionTokenController10001 = 非法登录信息
systemExceptionTokenController10002 = 注册成功
systemExceptionTokenController10003 = 密码重置成功
systemExceptionSysAppLoginServiceImpl10001 = 邮箱不可为空
systemExceptionSysAppLoginServiceImpl10002 = 账号或者密码错误
systemExceptionSysAppLoginServiceImpl10003 = 账号或者密码错误
systemExceptionSysAppLoginServiceImpl10004 = 邮箱已被使用
systemExceptionSysAppLoginServiceImpl10005 = 验证码无效
systemExceptionSysAppLoginServiceImpl10006 = 邮箱不存在
systemExceptionSysAppLoginServiceImpl10007 = 密码不一致
systemExceptionSysAppLoginServiceImpl10008 = 新密码不能与旧密码一致
systemExceptionSysAppLoginServiceImpl10009 = 当前登录用户异常,请重新登录或联系管理
systemExceptionSysAppLoginServiceImpl10010 = 令牌已过期
systemExceptionSysAppLoginServiceImpl10011 = 颁发者无效
systemExceptionSysAppLoginServiceImpl10012 = 无效访问群体
systemExceptionSysAppLoginServiceImpl10013 = 令牌签名无效
systemExceptionSysAppLoginServiceImpl10014 = 验证令牌失败
systemExceptionSysAppLoginServiceImpl10015 = 电子邮件未验证
systemExceptionSysAppLoginServiceImpl10016 = 无效的Google ID令牌
systemExceptionAuthFilter10001 = 令牌不能为空
systemExceptionAuthFilter10002 = 令牌已过期或验证不正确
systemExceptionAuthFilter10003 = 登录状态已过期
systemExceptionAuthFilter10004 = 令牌验证失败
systemVerificationEmailController10001 = 验证码已发送到你的邮箱
systemVerificationEmailController10002 = 发送验证码失败,请稍后重试
systemVerificationEmailController10003 = 验证码验证成功
systemVerificationEmailController10004 = 验证码无效或已过期
SystemCommandOverhaulAppController10001 = 操作成功
systemEmailUtil10001 = 发送邮件失败
systemR10001 = 操作成功
#管理端
# manager.页面,字段 = 用户管理
VerificationEmailTiTle = 你的验证码
VerificationEmailContent = 你的验证码是: {0},有效期为 {1} 分钟。
#APP端
AppSystemPassWordTip10001 = 两次输入不一致
AppSystemPassWordTip10002 = 不得包含您的姓名或电子邮件
AppSystemPassWordTip10003 = 至少包含8个字符
AppSystemPassWordTip10004 = 包含符号或数字
OnboardingOnboarding2ActionSkip = 跳过
OnboardingOnboarding2TitleOne = 每次都能智能、快速地规划路线。
OnboardingOnboarding2InfoOne = 优化后的导航将帮助您更快、更安全地到达目的地。
OnboardingOnboarding2ButtonGetStarted = 开始
OnboardingOnboarding3ActionSkip = 跳过
OnboardingOnboarding3TitleOne = 保持合规,驾驶平稳。
OnboardingOnboarding3InfoOne = 无缝的 EWD 和 CoR 合规性设计,助您保持行稳致远。
OnboardingOnboarding3ButtonGetStarted = 开始
OnboardingGetStartedTitleOne = 实现收益最大化,减少麻烦事。
OnboardingGetStartedInfoOne = 点击“注册”或“登录”以查看您附近正在发生的事情
OnboardingGetStartedButtonEamil = 继续使用电子邮件
OnboardingGetStartedButtonGoogle = 继续使用 Google
OnboardingGetStartedButtonApple = 继续使用 AppleID
AuthenticationLoginEmptyStateTitleOne = 登录至 Routez
AuthenticationLoginEmptyStateInfoOne = 欢迎回来!请填写您的详细信息。
AuthenticationLoginEmptyStatePlaceholderEamil = 邮箱
AuthenticationLoginEmptyStatePlaceholderPassword = 密码
AuthenticationLoginEmptyStateInfoTwo = 忘记了密码?
AuthenticationLoginEmptyStateActionReset = 重置
AuthenticationLoginEmptyStateButtonSignin = 登录
AuthenticationLoginEmptyStateButtonGoogle = 使用 Google 注册
AuthenticationLoginEmptyStateButtonApple = 使用 AppleID 注册
AuthenticationLoginEmptyStateInfoThree = 还没有账户吗?
AuthenticationLoginEmptyStateActionSignup = 注册
AuthenticationSignUpEmptyStateTitleOne = 注册
AuthenticationSignUpEmptyStatePlaceholderFullname = 全名
AuthenticationSignUpEmptyStatePlaceholderEamil = 邮箱
AuthenticationSignUpEmptyStatePlaceholderPassword = 密码
AuthenticationSignUpEmptyStatePlaceholderFullnameTips = 请检查全名
AuthenticationSignUpEmptyStatePlaceholderEamilTips = 请检查邮箱
AuthenticationSignUpEmptyStatePlaceholderPasswordTips = 请检查密码
AuthenticationSignUpEmptyStateInfoOne = 通过注册,您即表示同意我们的条款。
AuthenticationSignUpEmptyStateLegalService = 服务条款
AuthenticationSignUpEmptyStateInfoTwo =
AuthenticationSignUpEmptyStateLegalpolicy = 隐私协议。
AuthenticationSignUpEmptyStateButtonSignup = 注册
AuthenticationSignUpEmptyStateButtonGoogle = 使用 Google 注册
AuthenticationSignUpEmptyStateButtonApple = 使用 AppleID 注册
AuthenticationSignUpEmptyStateInfoThree = 已经有账号了吗?
AuthenticationSignUpEmptyStateActionSignin = 登录
AuthenticationEnterVerificationCodeTitleOne = 即将完成!
AuthenticationEnterVerificationCodeInfoOne = 请查看您的电子邮件收件箱,并输入验证码以验证您的账户。
AuthenticationEnterVerificationCodeButtonContinue = 继续
AuthenticationEnterVerificationCodeButtonResend = 重新发送验证码
AuthenticationForgotPasswordTitleOne = 无法登录?
AuthenticationForgotPasswordInfoOne = 输入你账号绑定的邮箱Carline 就会给你发个链接,直接重置密码!
AuthenticationForgotPasswordPlaceholderEamil = 邮箱
AuthenticationForgotPasswordButtonReset = 重置密码
AuthenticationForgotPasswordButtonReturn = 返回登录页面
AuthenticationEnterNewPasswordTitleOne = 创建您的新密码
AuthenticationEnterNewPasswordInfoOne = 您的新密码必须与之前的密码不同。
AuthenticationEnterNewPasswordPlaceholderPassword = 密码
AuthenticationEnterNewPasswordPlaceholderRepeat = 密码
AuthenticationEnterNewPasswordAlertOne = 不得包含您的姓名或电子邮件。
AuthenticationEnterNewPasswordAlertTwo = 至少 8 个字符
AuthenticationEnterNewPasswordAlertThree = 包含一个符号或一个数字
AuthenticationEnterNewPasswordButtonCreate = 创建新密码
DriverMyProfileTitleOne = 资料
DriverMyProfileButtonMyProfile = 我的资料
DriverMyProfileTitleTwo = 通用
DriverMyProfileActionOne = 职务
DriverMyProfileActionTwo = 试驾
DriverMyProfileActionThree = 我的订单
DriverMyProfileTitleThree = 浏览历史
DriverMyProfileNavMome = 主页
DriverMyProfileNavFavourite = 地图
DriverMyProfileNavMessage = 车队
DriverMyProfileNavProfile = 资料
DriverPreTripCheckTitleOne = 行车前检查
DriverPreTripCheckTitleTwo = 下一站
DriverPreTripCheckButtonStart = 开始检查
DriverPreTripCheckButtonOne = 检查完成
DriverPreTripCheckButtonFinish = 结束检查
DriverPreTripCheckButtonStartfull = 开始全部检查
DriverPreTripCheckListTitleOne = 行车前检查
DriverPreTripCheckListInfoOne = 完成
DriverPreTripCheckDetailInfoOne = 检查点
DriverPreTripCheckDetailTitleOne = 所需规格
DriverPreTripCheckDetailButtonFail = 失败
DriverPreTripCheckDetailButtonPass = 通过
DriverPreTripCheckDetailLabelOne = 问题详情
DriverPreTripCheckDetailTitleTwo = 照片
DriverPreTripCheckDetailButtonAdd = 添加照片
DriverPreTripCheckDetailButtonSubmit = 提交
DriverPreTripCheckHistoryTitleOne = 服务时间
DriverPreTripCheckHistoryInfoOne = 上次休息
DriverPreTripCheckHistoryInfoTwo = 下次需要
DriverPreTripCheckHistoryTitleTwo = 当前路线
DriverPreTripCheckHistoryTitleThree = 当前车辆
DriverPreTripCheckHistoryButtonStartInspection = 开始检查
DriverPreTripCheckHistoryButtonViewDetails = 查看详情
DriverPreTripCheckHistoryTitleFour = 最近检查
DriverPreTripCheckHistoryStatusIssues = 发现问题
DriverPreTripCheckHistoryButtonView = 查看
DriverJobCenterTitleOne = 卡车
DriverJobCenterStatusCompleted = 已完成
DriverJobCenterStatusReamining = 剩余
DriverJobCenterTitleTwo = 下一个任务
DriverJobCenterInfoPackages = 个包裹
DriverJobCenterButtonDeliverTo = 送至主码头
DriverJobCenterTitleThree = 即将到达的站点
DriverJobCenterButtonSelect = 选择任务
DriverCurrentDeliveryTitleOne = 当前配送
DriverCurrentDeliveryTitleTwo = 包裹
DriverCurrentDeliveryTitleThree = 配送备注
DriverCurrentDeliveryButtonStartNav = 开始导航
DriverCurrentDeliveryCompleteTitleOne = 当前配送
DriverCurrentDeliveryCompleteTitleTwo = 包裹
DriverCurrentDeliveryCompleteTitleThree = 配送备注
DriverCurrentDeliveryCompleteButtonComplete = 完成配送
TruckTruckParameterTitleOne = 卡车详情
TruckTruckParameterTitleTwo = 载重能力
TruckTruckParameterTitleThree = 状态
TruckTruckParameterButtonAdd = 添加到车队
TruckVehicleProfileTitleOne = 添加我的车辆
TruckVehicleProfileInfoOne = 所选车辆将用于路线计算和导航。
TruckVehicleProfileTitleTwo = 卡车
TruckVehicleProfileTitleThree = 乘用车
TruckVehicleProfileButtonAdd = 添加车辆
# 新增
NavigationReferenceNavHomePlaceholderSearch = 去哪里?
NavigationReferenceNavHomeButtonHome =
NavigationReferenceNavHomeButtonDepo = 仓库
NavigationReferenceNavHomeButtonSaved = 已保存
NavigationReferenceNavDockUpPlaceholderSearch = 去哪里?
NavigationReferenceNavDockUpButtonHome =
NavigationReferenceNavDockUpButtonDepo = 仓库
NavigationReferenceNavDockUpButtonAddPlace = 添加地点
NavigationReferenceNavDockUpTitleSaved = 已保存的位置
NavigationReferenceNavDockUpActionSeeAll = 查看全部
NavigationReferenceNavDockUpButtonAddJobLocation = 添加工作地点
NavigationReferenceNavSearchNormalPlaceholderSearch = 去哪里?
NavigationReferenceNavSearchNormalActionCancel = 取消
NavigationReferenceNavSearchNormalButtonRestaurant = 餐厅
NavigationReferenceNavSearchNormalButtonCoffee = 咖啡厅
NavigationReferenceNavSearchNormalButtonATM = ATM取款机
NavigationReferenceNavSearchNormalButtonShopping = 购物
NavigationReferenceNavSearchNormalButtonFuel = 加油
NavigationReferenceNavSearchNormalTitleRecentSearches = 最近
NavigationReferenceNavSearchNormalActionClear = 清除
NavigationReferenceNavSearchSelectPlaceholderYourLocation = 你的位置
NavigationReferenceNavSearchSelectButtonTruck = 卡车
NavigationReferenceNavSearchSelectButtonVan = 货车
NavigationReferenceNavSearchSelectButtonCar = 汽车
NavigationReferenceNavSearchSelectTitleLeavingNow = 立即出发
NavigationReferenceNavSearchSelectActionRoutePreferences = 路线偏好
NavigationReferenceNavRoutePreferenceTitleRoutePreferences = 路线偏好
NavigationReferenceNavRoutePreferenceinfoCar = 汽车
NavigationReferenceNavRoutePreferenceToggleSetMaxVehicleSpeed = 设置最大车速
NavigationReferenceNavRoutePreferenceInfoOne = 次速度将用于计算旅程时间,并在超速时提醒您
NavigationReferenceNavRoutePreferenceTitleZoneRegulations = 区域规定
NavigationReferenceNavRoutePreferenceMenuLicensePlateNumber = 车牌号码
NavigationReferenceNavRoutePreferenceInfoTwo = 印度尼西亚和菲律宾
NavigationReferenceNavRoutePreferenceTitleAVOID = 避免
NavigationReferenceNavRoutePreferenceCheckboxAvoidUTurns = 避免U型转弯
NavigationReferenceNavRoutePreferenceCheckboxAvoidFerries = 避免杜伦
NavigationReferenceNavRoutePreferenceCheckboxAvoidHighways = 避免高速公路
NavigationReferenceNavRoutePreferenceCheckboxAvoidTunnels = 避免隧道
NavigationReferenceNavRoutePreferenceCheckboxAvoidTollRoads = 避免收费公路
NavigationReferenceNavRoutePreferenceCheckboxAvoidUnpavedRoads = 避免未铺装道路
NavigationReferenceNavigatingTitleArrival = 到达
NavigationReferenceNavigatingTitleRemaining = 剩余
NavigationReferenceNavigatingTitleDistance = 距离
NavigationReferenceNavOverviewTitleArrival = 到达
NavigationReferenceNavOverviewTitleAddAStop = 添加站点
NavigationReferenceNavSearchSelectButtonSearch = 搜索
NavigationReferenceNavSearchSelectButtonGas = 加油站
NavigationReferenceNavSearchSelectButtonParking = 停车场
NavigationReferenceNavSearchSelectButtonRestaurants = 餐厅
NavigationReferenceNavOverviewTitleQuickSettings = 快速设置
NavigationReferenceNavOverviewToggleSpeedAlert = 超速报警
NavigationReferenceNavOverviewToggleOffline = 离线模式
NavigationReferenceNavOverviewToggleTheme = 主题
NavigationReferenceNavOverviewButtonMoreSetting = 更多设置
NavigationReferenceNavOverviewTitleOverview = 概览
NavigationReferenceHomePageMenuStartDailyRoute = 开始每日路线
NavigationReferenceHomePageMenuCurrentRoute = 当前路线
NavigationReferenceHomePageInfoNoActiveRoute = 无活动路线
NavigationReferenceHomePageMenuMyDiary = 我的日记 / 日志
NavigationReferenceHomePageInfoElectronicWorkDiary = 电子工作日记
NavigationReferenceHomePageMenuTripsReports = 行程和报告
NavigationReferenceHomePageInfoViewTripHistory = 查看历史行程
NavigationReferenceHomePageMenuDefectsMaintenance = 缺陷与维护
NavigationReferenceHomePageInfoVehicleIssues = 车辆问题
NavigationReferenceSetVehicleInfoTitleDimensionsWeight = 尺寸和重量
NavigationReferenceSetVehicleInfoTitleVehicleSpecifications = 车辆规格
NavigationReferenceSetVehicleInfoinfoOne = 输入包括拖车和负载在内的总尺寸和重量
NavigationReferenceSetVehicleInfoTitlePhysicalDimensions = 物理尺寸
NavigationReferenceSetVehicleInfoTitleUnitM =
NavigationReferenceSetVehicleInfoTitleUnitCM = 厘米
NavigationReferenceSetVehicleInfoTitleHeight = 高度
NavigationReferenceSetVehicleInfoTitleWidth = 宽度
NavigationReferenceSetVehicleInfoTitleLength = 长度
NavigationReferenceSetVehicleInfoTitleWeightSpecifications = 重量规格
NavigationReferenceSetVehicleInfoTitleTotalWeight = 总重量
NavigationReferenceSetVehicleInfoTitleUnitTonnes =
NavigationReferenceSetVehicleInfoInfoTwo = 15吨 = 33,069 磅
NavigationReferenceSetVehicleInfoButtonNext = 下一项:危险品
NavigationReferenceHazardousMaterialsTitleHazardousMaterials = 危险材料
NavigationReferenceHazardousMaterialsTitleCargoClassification = 货物分类
NavigationReferenceHazardousMaterialsInfoOne = 选择您将要运输的任何危险材料
NavigationReferenceHazardousMaterialsTitleHazmatCategories = 危险品类别
NavigationReferenceHazardousMaterialsToggleExplosives = 爆炸物
NavigationReferenceHazardousMaterialsToggleGas = 气体
NavigationReferenceHazardousMaterialsToggleFlammable = 易燃物
NavigationReferenceHazardousMaterialsTogglePoison = 毒物
NavigationReferenceHazardousMaterialsToggleRadioactive = 放射性物质
NavigationReferenceHazardousMaterialsToggleCorrosive = 腐蚀性物质
NavigationReferenceHazardousMaterialsToggleMiscellaneous = 杂项
NavigationReferenceHazardousMaterialsButtonNext = 下一项:隧道类别
NavigationReferenceTripSummaryTitleTripSummary = 行程摘要
NavigationReferenceTripSummaryTitleTripCompletedSuccessfully = 行程成功完成
NavigationReferenceTripSummaryInfoOne = 所有合规要求已满足
NavigationReferenceTripSummaryTitleTripStatistics = 行程统计
NavigationReferenceTripSummaryTitleStartTime = 开始时间
NavigationReferenceTripSummaryTitleEndTime = 结束时间
NavigationReferenceTripSummaryTitleDistance = 距离
NavigationReferenceTripSummaryTitleDuration = 持续时间
NavigationReferenceTripSummaryTitleComplianceStatus = 合规状态
NavigationReferenceTripSummaryStatusOne = 行程前检查已完成
NavigationReferenceTripSummaryStatusTwo = 路线配置正确
NavigationReferenceTripSummaryStatusThree = 休息要求已满足
NavigationReferenceTripSummaryStatusFour = 未报告任何事件
NavigationReferenceTripSummaryButtonPDF = PDF
NavigationReferenceTripSummaryButtonDone = 完成
TripReportAndProfileDefectsMaintenanceButtonBack = 返回
TripReportAndProfileDefectsMaintenanceTitleDefectsMaintenance = 缺陷与维护
TripReportAndProfileDefectsMaintenanceTabOpen = 打开
TripReportAndProfileDefectsMaintenanceTabResolved = 已解决
TripReportAndProfileDefectsMaintenanceTitleReported = 已报告
TripReportAndProfileDefectsMaintenanceTitleLoaction = 位置
TripReportAndProfileDefectsMaintenanceTitleReporter = 报告人
TripReportAndProfileDefectsMaintenanceButtonAddNewDefect = 添加新缺陷
TripReportAndProfileMyDiaryLogButtonBack = 返回
TripReportAndProfileMyDiaryLogTitleMyDiaryLog = 我的日记 / 日志
TripReportAndProfileMyDiaryLogTabToday = 今天
TripReportAndProfileMyDiaryLogTabWeek =
TripReportAndProfileMyDiaryLogTab28Days = 28天
TripReportAndProfileMyDiaryLogTitleDailyTotals = 每日总计
TripReportAndProfileMyDiaryLogTitleDrivingTime = 驾驶时间
TripReportAndProfileMyDiaryLogTitleOnDutyTime = 值班时间
TripReportAndProfileMyDiaryLogTitleOffDutyTime = 下班时间
TripReportAndProfileMyDiaryLogTitleTotalBreaks = 总休息时间
TripReportAndProfileMyDiaryLogStatusRestDay = 休息日
TripReportAndProfileMyDiaryLogTitleOffDuty = 下班时间
TripReportAndProfileMyDiaryLogTitleDriving = 驾驶时间
TripReportAndProfileMyDiaryLogTitleOnDuty = 在岗时间
TripReportAndProfileMyDiaryLogStatusCompliant = 合规
TripReportAndProfileMyDiaryLogStatusViolation = 违规
TripReportAndProfileMyDiaryLogTitle28DayComplianceOverview = 28天合规概览
TripReportAndProfileMyDiaryLogStatusOff = 下班
TripReportAndProfileMyDiaryLogButtonExport = 导出
TripReportAndProfileMyDiaryLogButtonEmailReport = 电子邮件报告
TripReportAndProfileTripsReportsButtonBack = 返回
TripReportAndProfileTripsReportsTitleTripsReports = 行程 & 报告
TripReportAndProfileTripsReportsTabRecent = 最近
TripReportAndProfileTripsReportsTabThisWeek = 本周
TripReportAndProfileTripsReportsTabThisMonth = 本月
TripReportAndProfileTripsReportsTitleTotalTrips = 总行程数
TripReportAndProfileTripsReportsTitleDistance = 距离
TripReportAndProfileTripsReportsTitleCompliance = 合规性
TripReportAndProfileTripsReportsTitleDuration = 持续时间
TripReportAndProfileTripsReportsTitleLoadType = 负载类型
TripReportAndProfileTripsReportsActionViewDetails = 查看详情
TripReportAndProfileTripsReportsButtonExportAl = 导出 AI
TripReportAndProfileTripsReportsButtonFilter = 筛选
TripReportAndProfileMyProfileDetailButtonBack = 返回
TripReportAndProfileMyProfileDetailTitleProfile = 个人资料
TripReportAndProfileMyProfileDetailActionEdit = 编辑
TripReportAndProfileMyProfileDetailTitleDriverInformation = 驾驶员信息
TripReportAndProfileMyProfileDetailTitleLicenceNumber = 驾照号码
TripReportAndProfileMyProfileDetailTitleState =
TripReportAndProfileMyProfileDetailTitleExpiryDate = 过期日期
TripReportAndProfileMyProfileDetailTitleABN = ABN
TripReportAndProfileMyProfileDetailTitleMyVehicles = 我的车辆
TripReportAndProfileMyProfileDetailTitleAccountSettings = 账户设置
TripReportAndProfileMyProfileDetailTitlePushNotifications = 推送通知
TripReportAndProfileMyProfileDetailTitleLocationServices = 位置服务
TripReportAndProfileMyProfileDetailTitleAutoBackup = 自动备份
TripReportAndProfileMyProfileDetailButtonSettings = 设置
TripReportAndProfileMyProfileDetailButtonSign Out = 退出登录
TripReportAndProfileTripInProgressTitleTripInProgress = 行程进行中
TripReportAndProfileTripInProgressActionActive = 活动
TripReportAndProfileTripInProgressTitleCurrentLocation = 当前位置
TripReportAndProfileTripInProgressTitleDestination = 目的地
TripReportAndProfileTripInProgressTitleTripDuration = 行程时长
TripReportAndProfileTripInProgressTitleWorkTime = 工作时间
TripReportAndProfileTripInProgressTitleDriveTime = 驾驶时间
TripReportAndProfileTripInProgressTitleRestTaken = 休息时间
TripReportAndProfileTripInProgressTitleQuickActions = 快捷操作
TripReportAndProfileTripInProgressButtonTakeBreak = 休息
TripReportAndProfileTripInProgressButtonReportlssue = 报告问题
TripReportAndProfileTripInProgressButtonFuelStop = 加油
TripReportAndProfileTripInProgressButtonAddNote = 添加备注
TripReportAndProfileTripInProgressButtonEndTrip = 结束行程
TripReportAndProfileEndTripButtonBack = 返回
TripReportAndProfileEndTripTitleEndTrip = 结束行程
TripReportAndProfileEndTripTitleTripSummary = 行程摘要
TripReportAndProfileEndTripTitleStartLocation = 起点位置
TripReportAndProfileEndTripTitleEndLocation = 结束位置
TripReportAndProfileEndTripTitleTotalDistance = 总距离
TripReportAndProfileEndTripTitleTripDuration = 行程时长
TripReportAndProfileEndTripTitleFuelUsed = 已用燃油(估计)
TripReportAndProfileEndTripTitleFinalOdometerLu = 最终里程表读数
TripReportAndProfileEndTripTitleStartOdometer = 开始里程表读数
TripReportAndProfileEndTripTitleEndOdometer = 结束里程表读数
TripReportAndProfileEndTripTitleDeliveryConfirmation = 交付确认
TripReportAndProfileEndTripTitleCargoDeliveredSuccessfully = 货物成功交付
TripReportAndProfileEndTripTitleDeliveryDocumentationComplete = 交付文件完成
TripReportAndProfileEndTripTitleVehicleParkedSafely = 车辆安全停放
TripReportAndProfileEndTripPlaceholderAddTripNotes = 添加行程备注(可选)
TripReportAndProfileEndTripButtonCompleteTrip = 完成行程
TripReportAndProfileBreakRestButtonBack = 返回
TripReportAndProfileBreakRestTitleBreakRest = 休息与恢复
TripReportAndProfileBreakRestTitleBreakTimer = 休息计时器
TripReportAndProfileBreakRestInfoOne = 至少
TripReportAndProfileBreakRestInfoTwo = 所需
TripReportAndProfileBreakRestTitleWorkTimeSummary = 工作时间总结
TripReportAndProfileBreakRestTitleTotalWorkTime = 总工作时间
TripReportAndProfileBreakRestTitleContinuousDriving = 连续驾驶时间
TripReportAndProfileBreakRestTitleLastBreak = 上次休息时间
TripReportAndProfileBreakRestTitleTotalBreaksToday = 今天总休息时间
TripReportAndProfileBreakRestTitleComplianceStatus = 合规状态
TripReportAndProfileBreakRestTitleDailyWorkLimit = 每日工作限制
TripReportAndProfileBreakRestTitleWeeklyHours = 每周工时
TripReportAndProfileBreakRestButtonEndBreakResume = 结束休息并恢复工作
TripReportAndProfileTripCompleteTitleTripComplete = 行程完成!
TripReportAndProfileTripCompleteInfoOne = 您的行程已成功记录并保存到您的电子工作日记中。
TripReportAndProfileTripCompleteTitleTripStatistics = 行程统计
TripReportAndProfileTripCompleteTitleDistance = 距离
TripReportAndProfileTripCompleteTitleDuration = 持续时间
TripReportAndProfileTripCompleteTitleAvgSpeed = 平均速度
TripReportAndProfileTripCompleteTitleFuelUsed = 燃油消耗
TripReportAndProfileTripCompleteTitleAllComplianceMet = 所有合规要求已满足
TripReportAndProfileTripCompleteInfoTwo = 您的行程符合所有NHVR规定
TripReportAndProfileTripCompleteButtonViewSummary = 查看摘要
TripReportAndProfileTripCompleteButtonBackToDashboard = 返回仪表板
TripReportAndProfileReportIncidentButtonBack = 返回
TripReportAndProfileReportIncidentTitleReportIncident = 报告事件
TripReportAndProfileReportIncidentStatusUrgent = 紧急
TripReportAndProfileReportIncidentTitleIncidentType = 事件类型
TripReportAndProfileReportIncidentTitleLocationDetails = 位置详情
TripReportAndProfileReportIncidentTitleCurrentLocation = 当前位置
TripReportAndProfileReportIncidentPlaceholderOne = 最近的地标或地址
TripReportAndProfileReportIncidentTitleIncidentDetails = 事件详情
TripReportAndProfileReportIncidentPlaceholderTwo = 描述发生了什么。
TripReportAndProfileReportIncidentTitleAnyInjuries = 是否有受伤?
TripReportAndProfileReportIncidentButtonYes =
TripReportAndProfileReportIncidentButtonNo =
TripReportAndProfileReportIncidentTitleEmergencyServicesCalled = 是否呼叫了紧急服务?
TripReportAndProfileReportIncidentTitlePhotosEvidence = 照片和证据
TripReportAndProfileReportIncidentButtonSubmitReport = 提交报告
TripReportAndProfileReportIncidentButtonSaveDraft = 保存草稿
TripReportAndProfileDailySummaryButtonBack = 返回
TripReportAndProfileDailySummaryTitleDailySummary = 每日总结
TripReportAndProfileDailySummaryInfoOne = 工作日完成情况
TripReportAndProfileDailySummaryTitleTodayPerformance = 今日表现
TripReportAndProfileDailySummaryTitleTotalDistance = 总距离
TripReportAndProfileDailySummaryTitleTotalWorkTime = 总工作时间
TripReportAndProfileDailySummaryTitleTripsCompleted = 完成的行程次数
TripReportAndProfileDailySummaryTitleTotalBreaks = 总休息时间
TripReportAndProfileDailySummaryTitleTripDetails = 行程详情
TripReportAndProfileDailySummaryTitleTrip = 行程
TripReportAndProfileDailySummaryTitleComplianceSummary = 合规总结
TripReportAndProfileDailySummaryTitleWorkTimeLimits = 工作时间限制
TripReportAndProfileDailySummaryTitleRestBreaks = 休息时间
TripReportAndProfileDailySummaryTitleElectronicWorkDiary = 电子工作日记
TripReportAndProfileDailySummaryButtonExportReport = 导出报告
TripReportAndProfileDailySummaryButtonSignOffDay = 签退日
AuthenticationAddDriverTitleAddVehicle = 添加车辆
AuthenticationAddDriverTitleVehicleInformation = 车辆信息
AuthenticationAddDriverTagRegistration = 车牌号码
AuthenticationAddDriverTagVIN = 车辆识别号(VIN)
AuthenticationAddDriverTagRegistrationExpiry = 注册到期日志
AuthenticationAddDriverTagInsuranceExpiry = 保险到期日期
AuthenticationAddDriverTagVehicleType = 车辆类型
AuthenticationAddDriverOptionSelectType = 选择类型
AuthenticationAddDriverTagMake = 品牌
AuthenticationAddDriverTagModel = 型号
AuthenticationAddDriverTagYear = 年份
AuthenticationAddDriverTagColor = 颜色
AuthenticationAddDriverTagDimensionsCapacity = 尺寸和容量
AuthenticationAddDriverTagVehicleHeight = 车辆高度
AuthenticationAddDriverTagVehicleWidth = 车辆宽度
AuthenticationAddDriverTagVehicleLength = 车辆长度
AuthenticationAddDriverTagGrossWeight = 总载重
AuthenticationAddDriverTitleAdditionalSpecifications = 附加规格
AuthenticationAddDriverTagAxles = 轴数
AuthenticationAddDriverTagTrailers = 拖车
AuthenticationAddDriverTagWeightPerAxle = 每轴重量
AuthenticationAddDriverTagTruckType = 卡车类型
AuthenticationAddDriverTitleHazardousMaterials = 危险品
AuthenticationAddDriverTagCarryingHazardousGoods = 运输危险品?
AuthenticationAddDriverButtonNo =
AuthenticationAddDriverButtonYes =
AuthenticationAddDriverButtonAddVehicle = 添加车辆
AuthenticationAddVehicleTitleDriverProfile = 驾驶员资料
AuthenticationAddVehicleTitlePersonalInformation = 个人信息
AuthenticationAddVehicleTagFirstName =
AuthenticationAddVehicleTagLastName = 姓氏
AuthenticationAddVehicleTagPhoneNumber = 电话号码
AuthenticationAddVehicleTitleLicenseCertification = 驾照和认证
AuthenticationAddVehicleTagDriverLicenseNumber = 驾照号码
AuthenticationAddVehicleTagLicenseClass = 驾照类别
AuthenticationAddVehicleTagLicenseState = 驾照状态
AuthenticationAddVehicleOptionSelect = 选择
AuthenticationAddVehicleTagLicenseExpiry = 驾照到期日期
AuthenticationAddVehicleTagMedicalCert = 医疗证明
AuthenticationAddVehicleTitleHazmatCertification = 危险品认证
AuthenticationAddVehicleTagHazmatCertified = 是否获得危险品认证?
AuthenticationAddVehicleButtonNo =
AuthenticationAddVehicleButtonYes =
AuthenticationAddVehicleButtonSave = 保存
AuthenticationHazmatDialogTitleHazmatCertification = 危险品认证
AuthenticationHazmatDialogTagHazmatCertified = 是否通过危险品认证?
AuthenticationHazmatDialogButtonNo =
AuthenticationHazmatDialogButtonYes =
AuthenticationHazmatDialogTagHazmatExpiryDate = 危险品有效期
AuthenticationHazmatDialogOptionSelect = 选择日期

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 读取yml配置文件中的路径地址-->
<springProperty scope="context" name="logPath" source="logging.pathUrl" defaultValue="logDir"/>
<!-- 日志存放路径 -->
<property name="log.path" value="${logPath}" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.vetti" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
</settings>
</configuration>

171
vetti-common/pom.xml Normal file
View File

@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>vetti-service</artifactId>
<groupId>com.vetti</groupId>
<version>3.9.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>vetti-common</artifactId>
<description>
common通用工具
</description>
<dependencies>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- JSON工具类 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- yml解析器 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!-- Jaxb -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- redis 缓存操作 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- pool 对象池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
</dependency>
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 糊涂工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
<dependency>
<groupId>com.sendgrid</groupId>
<artifactId>sendgrid-java</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,199 @@
package com.vetti.common.ai;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.google.gson.Gson;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* 文本转换为语音
*/
public class ElevenLabsClient {
private static final String BASE_URL = "https://api.elevenlabs.io/v1";
private final String apiKey;
private final CloseableHttpClient httpClient;
private final Gson gson;
public ElevenLabsClient(String apiKey) {
this.apiKey = apiKey;
this.httpClient = HttpClients.createDefault();
this.gson = new Gson();
}
/**
* 将文本转换为语音并保存到文件
*
* @param text 要转换的文本
* @param voiceId 语音ID (可从ElevenLabs网站获取)
* @param outputFilePath 输出文件路径
* @throws IOException 网络请求或文件操作异常
*/
public void textToSpeech(String text, String voiceId, String outputFilePath) throws IOException {
HttpPost httpPost = new HttpPost(BASE_URL + "/text-to-speech/" + voiceId);
httpPost.setHeader("xi-api-key", apiKey);
httpPost.setHeader("Content-Type", "application/json");
Map<String, Object> payload = new HashMap<>();
payload.put("text", text);
payload.put("model_id", "eleven_monolingual_v1");
payload.put("voice_settings", new VoiceSettings(0.7, 0.5));
StringEntity entity = new StringEntity(gson.toJson(payload), ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
try (InputStream inputStream = responseEntity.getContent();
FileOutputStream outputStream = new FileOutputStream(outputFilePath)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
}
}
/**
* 获取可用的语音列表
*
* @return 语音列表响应
* @throws IOException 网络请求异常
*/
public VoicesResponse getVoices() throws IOException {
HttpGet httpGet = new HttpGet(BASE_URL + "/voices");
httpGet.setHeader("xi-api-key", apiKey);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity responseEntity = response.getEntity();
String responseBody = EntityUtils.toString(responseEntity);
return gson.fromJson(responseBody, VoicesResponse.class);
}
}
// 关闭HTTP客户端
public void close() throws IOException {
httpClient.close();
}
// 语音设置内部类
private static class VoiceSettings {
private double stability;
private double similarity_boost;
public VoiceSettings(double stability, double similarity_boost) {
this.stability = stability;
this.similarity_boost = similarity_boost;
}
// getter方法
public double getStability() {
return stability;
}
public double getSimilarity_boost() {
return similarity_boost;
}
}
// 语音列表响应模型
public static class VoicesResponse {
private Voice[] voices;
public Voice[] getVoices() {
return voices;
}
public void setVoices(Voice[] voices) {
this.voices = voices;
}
public static class Voice {
private String voice_id;
private String name;
private String category;
private FineTuning fine_tuning;
public String getVoice_id() {
return voice_id;
}
public void setVoice_id(String voice_id) {
this.voice_id = voice_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public FineTuning getFine_tuning() {
return fine_tuning;
}
public void setFine_tuning(FineTuning fine_tuning) {
this.fine_tuning = fine_tuning;
}
}
}
// 使用示例
public static void main(String[] args) {
String apiKey = "sk_5240d8f56cb1eb5225fffcf903f62479884d1af5b3de6812";
ElevenLabsClient client = new ElevenLabsClient(apiKey);
try {
// 获取可用语音
VoicesResponse voicesResponse = client.getVoices();
if (voicesResponse != null && voicesResponse.getVoices() != null && voicesResponse.getVoices().length > 0) {
System.out.println("Available voices:");
for (VoicesResponse.Voice voice : voicesResponse.getVoices()) {
System.out.println(voice.getName() + " (ID: " + voice.getVoice_id() + ")");
}
// 使用第一个可用语音进行文本转语音
String firstVoiceId = voicesResponse.getVoices()[0].getVoice_id();
String text = "Come on, Baby,Come on, Baby,Come on, Baby,Come on, Baby";
String outputFile = "output.mp3";
client.textToSpeech(text, firstVoiceId, outputFile);
System.out.println("Audio saved to: " + outputFile);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,23 @@
package com.vetti.common.ai;
public class FineTuning {
private String status;
private String model_id;
public String getModel_id() {
return model_id;
}
public void setModel_id(String model_id) {
this.model_id = model_id;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@@ -0,0 +1,159 @@
package com.vetti.common.ai;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 发送文本消息到指定的WhatsApp
*/
public class WapiAiClient {
private static final String BASE_URL = "https://wapi.ai/api";
private final String apiKey;
private final CloseableHttpClient httpClient;
private final Gson gson;
public WapiAiClient(String apiKey) {
this.apiKey = apiKey;
this.httpClient = HttpClients.createDefault();
this.gson = new Gson();
}
/**
* 发送文本消息到指定的WhatsApp号码
* @param phoneNumber 目标电话号码(带国家代码,如:+1234567890)
* @param message 要发送的消息内容
* @return API响应
* @throws IOException 网络请求异常
*/
public WapiResponse sendTextMessage(String phoneNumber, String message) throws IOException {
HttpPost httpPost = new HttpPost(BASE_URL + "/sendMessage");
httpPost.setHeader("Authorization", "Bearer " + apiKey);
httpPost.setHeader("Content-Type", "application/json");
Map<String, Object> payload = new HashMap<>();
payload.put("phone", phoneNumber);
payload.put("body", message);
StringEntity entity = new StringEntity(gson.toJson(payload), ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
HttpEntity responseEntity = response.getEntity();
String responseBody = EntityUtils.toString(responseEntity);
return gson.fromJson(responseBody, WapiResponse.class);
}
}
/**
* 获取最近的消息
* @param limit 消息数量限制
* @return 消息列表
* @throws IOException 网络请求异常
*/
public WapiMessagesResponse getRecentMessages(int limit) throws IOException {
HttpGet httpGet = new HttpGet(BASE_URL + "/messages?limit=" + limit);
httpGet.setHeader("Authorization", "Bearer " + apiKey);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity responseEntity = response.getEntity();
String responseBody = EntityUtils.toString(responseEntity);
return gson.fromJson(responseBody, WapiMessagesResponse.class);
}
}
// 关闭HTTP客户端
public void close() throws IOException {
httpClient.close();
}
// 响应模型类
public static class WapiResponse {
private boolean success;
private String message;
private Object data;
// getter和setter方法
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Object getData() { return data; }
public void setData(Object data) { this.data = data; }
}
// 消息响应模型类
public static class WapiMessagesResponse {
private boolean success;
private Message[] messages;
// getter和setter方法
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public Message[] getMessages() { return messages; }
public void setMessages(Message[] messages) { this.messages = messages; }
public static class Message {
private String id;
private String phone;
private String body;
private String type;
private String timestamp;
// getter和setter方法
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getTimestamp() { return timestamp; }
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
}
}
// 使用示例
public static void main(String[] args) {
String apiKey = "your_wapi_ai_api_key";
WapiAiClient client = new WapiAiClient(apiKey);
try {
// 发送消息
WapiResponse response = client.sendTextMessage("+1234567890", "Hello from Wapi.ai Java Client!");
System.out.println("Message sent: " + response.isSuccess());
// 获取最近消息
WapiMessagesResponse messagesResponse = client.getRecentMessages(5);
if (messagesResponse.isSuccess() && messagesResponse.getMessages() != null) {
System.out.println("Recent messages:");
for (WapiMessagesResponse.Message msg : messagesResponse.getMessages()) {
System.out.println(msg.getPhone() + ": " + msg.getBody());
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,113 @@
package com.vetti.common.ai;
import cn.hutool.json.JSONObject;
import okhttp3.*;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class WhisperClient {
private static final String API_URL = "https://api.openai.com/v1/audio/transcriptions";
private static final String MODEL = "whisper-1";
private final String apiKey;
private final OkHttpClient client;
// 构造函数传入API密钥
public WhisperClient(String apiKey) {
this.apiKey = apiKey;
this.client = new OkHttpClient();
}
/**
* 将音频文件转换为文本
* @param audioFile 音频文件
* @param options 可选参数 (language, response_format等)
* @return 转换后的文本
* @throws IOException 网络或文件操作异常
* @throws WhisperException API返回错误
*/
public String transcribe(File audioFile, Map<String, String> options) throws IOException, WhisperException {
// 验证文件是否存在
if (!audioFile.exists()) {
throw new IllegalArgumentException("音频文件不存在: " + audioFile.getAbsolutePath());
}
// 构建请求体
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("model", MODEL)
.addFormDataPart("file", audioFile.getName(),
RequestBody.create(audioFile, MediaType.parse("audio/*")));
// 添加可选参数
if (options != null) {
for (Map.Entry<String, String> entry : options.entrySet()) {
bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
}
}
// 构建请求
Request request = new Request.Builder()
.url(API_URL)
.header("Authorization", "Bearer " + apiKey)
.post(bodyBuilder.build())
.build();
// 发送请求并处理响应
try (Response response = client.newCall(request).execute()) {
String responseBody = response.body().string();
if (!response.isSuccessful()) {
throw new WhisperException(
"API请求失败: " + response.code() +
", 详情: " + responseBody
);
}
// 解析JSON响应
JSONObject json = new JSONObject(responseBody);
return "";
}
}
/**
* 简化的转写方法,使用默认参数
*/
public String transcribe(File audioFile) throws IOException, WhisperException {
return transcribe(audioFile, null);
}
// 自定义异常类用于处理Whisper API相关错误
public static class WhisperException extends Exception {
public WhisperException(String message) {
super(message);
}
}
// 使用示例
public static void main(String[] args) {
// 替换为你的API密钥
String apiKey = "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";
WhisperClient client = new WhisperClient(apiKey);
// 音频文件路径
File audioFile = new File("/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/output.mp3");
try {
// 添加可选参数,例如指定语言为中文
Map<String, String> options = new HashMap<>();
options.put("language", "zh");
String result = client.transcribe(audioFile, options);
System.out.println("转写结果: " + result);
} catch (IOException e) {
System.err.println("IO错误: " + e.getMessage());
e.printStackTrace();
} catch (WhisperException e) {
System.err.println("Whisper API错误: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.err.println("参数错误: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,72 @@
package com.vetti.common.ai;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import cn.hutool.json.JSONUtil;
public class WhisperTest {
private static final String API_KEY = "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";
private static final String AUDIO_PATH = "/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/output.mp3"; // 替换为你的音频文件路径
public static void main(String[] args) throws Exception {
// 1. 检查音频文件是否存在
if (!Files.exists(Paths.get(AUDIO_PATH))) {
System.err.println("音频文件不存在: " + AUDIO_PATH);
return;
}
// 2. 构建multipart/form-data请求上传文件
MultipartBodyPublisher multipart = new MultipartBodyPublisher();
multipart.addPart("model", "whisper-1");
multipart.addFilePart("file", AUDIO_PATH);
// 3. 发送请求
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.openai.com/v1/audio/transcriptions"))
.header("Authorization", "Bearer " + API_KEY)
.POST(multipart.build())
.build();
// 4. 获取并打印完整响应
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("状态码: " + response.statusCode());
System.out.println("响应体: " + response.body());
// 5. 解析结果(如果成功)
if (response.statusCode() == 200) {
System.out.println("识别结果: " + JSONUtil.toJsonStr(response.body()));
}
}
}
// 辅助类处理multipart/form-data格式
class MultipartBodyPublisher {
private final Map<String, String> parts = new HashMap<>();
private String filePartName;
private String filePath;
public void addPart(String name, String value) {
parts.put(name, value);
}
public void addFilePart(String name, String path) {
this.filePartName = name;
this.filePath = path;
}
public HttpRequest.BodyPublisher build() throws Exception {
// 实际项目中建议使用成熟的HTTP客户端如OkHttp处理multipart此处为简化示例
// 完整实现需处理边界、文件流等可参考https://stackoverflow.com/a/65271748
return HttpRequest.BodyPublishers.ofString(""); // 仅示例框架,需完善
}
}

View File

@@ -0,0 +1,19 @@
package com.vetti.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 匿名访问不鉴权注解
*
* @author ruoyi
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous
{
}

View File

@@ -0,0 +1,33 @@
package com.vetti.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据权限过滤注解
*
* @author ruoyi
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
/**
* 部门表的别名
*/
public String deptAlias() default "";
/**
* 用户表的别名
*/
public String userAlias() default "";
/**
* 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取多个权限用逗号分隔开来
*/
public String permission() default "";
}

View File

@@ -0,0 +1,28 @@
package com.vetti.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.vetti.common.enums.DataSourceType;
/**
* 自定义多数据源切换注解
*
* 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
*
* @author ruoyi
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
/**
* 切换数据源名称
*/
public DataSourceType value() default DataSourceType.MASTER;
}

View File

@@ -0,0 +1,197 @@
package com.vetti.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.vetti.common.utils.poi.ExcelHandlerAdapter;
/**
* 自定义导出Excel数据注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{
/**
* 导出时在excel中排序
*/
public int sort() default Integer.MAX_VALUE;
/**
* 导出到Excel中的名字.
*/
public String name() default "";
/**
* 日期格式, 如: yyyy-MM-dd
*/
public String dateFormat() default "";
/**
* 如果是字典类型请设置字典的type值 (如: sys_user_sex)
*/
public String dictType() default "";
/**
* 读取内容转表达式 (如: 0=男,1=女,2=未知)
*/
public String readConverterExp() default "";
/**
* 分隔符,读取字符串组内容
*/
public String separator() default ",";
/**
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
*/
public int scale() default -1;
/**
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
*/
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/**
* 导出时在excel中每个列的高度
*/
public double height() default 14;
/**
* 导出时在excel中每个列的宽度
*/
public double width() default 16;
/**
* 文字后缀,如% 90 变成90%
*/
public String suffix() default "";
/**
* 当值为空时,字段的默认值
*/
public String defaultValue() default "";
/**
* 提示信息
*/
public String prompt() default "";
/**
* 是否允许内容换行
*/
public boolean wrapText() default false;
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
*/
public boolean comboReadDict() default false;
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
public boolean needMerge() default false;
/**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/
public boolean isExport() default true;
/**
* 另一个类中的属性名称,支持多级获取,以小数点隔开
*/
public String targetAttr() default "";
/**
* 是否自动统计数据,在最后追加一行统计数据总和
*/
public boolean isStatistics() default false;
/**
* 导出类型0数字 1字符串 2图片
*/
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出列头背景颜色
*/
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
/**
* 导出列头字体颜色
*/
public IndexedColors headerColor() default IndexedColors.WHITE;
/**
* 导出单元格背景颜色
*/
public IndexedColors backgroundColor() default IndexedColors.WHITE;
/**
* 导出单元格字体颜色
*/
public IndexedColors color() default IndexedColors.BLACK;
/**
* 导出字段对齐方式
*/
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
/**
* 自定义数据处理器
*/
public Class<?> handler() default ExcelHandlerAdapter.class;
/**
* 自定义数据处理器参数
*/
public String[] args() default {};
/**
* 字段类型0导出导入1仅导出2仅导入
*/
Type type() default Type.ALL;
public enum Type
{
ALL(0), EXPORT(1), IMPORT(2);
private final int value;
Type(int value)
{
this.value = value;
}
public int value()
{
return this.value;
}
}
public enum ColumnType
{
NUMERIC(0), STRING(1), IMAGE(2), TEXT(3);
private final int value;
ColumnType(int value)
{
this.value = value;
}
public int value()
{
return this.value;
}
}
}

View File

@@ -0,0 +1,18 @@
package com.vetti.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel注解集
*
* @author ruoyi
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Excels
{
public Excel[] value();
}

View File

@@ -0,0 +1,51 @@
package com.vetti.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.vetti.common.enums.BusinessType;
import com.vetti.common.enums.OperatorType;
/**
* 自定义操作日志记录注解
*
* @author ruoyi
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
}

View File

@@ -0,0 +1,40 @@
package com.vetti.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.vetti.common.constant.CacheConstants;
import com.vetti.common.enums.LimitType;
/**
* 限流注解
*
* @author ruoyi
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter
{
/**
* 限流key
*/
public String key() default CacheConstants.RATE_LIMIT_KEY;
/**
* 限流时间,单位秒
*/
public int time() default 60;
/**
* 限流次数
*/
public int count() default 100;
/**
* 限流类型
*/
public LimitType limitType() default LimitType.DEFAULT;
}

View File

@@ -0,0 +1,31 @@
package com.vetti.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解防止表单重复提交
*
* @author ruoyi
*
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{
/**
* 间隔时间(ms),小于此时间视为重复提交
*/
public int interval() default 5000;
/**
* 提示消息
*/
public String message() default "不允许重复提交,请稍候再试";
}

View File

@@ -0,0 +1,24 @@
package com.vetti.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.vetti.common.config.serializer.SensitiveJsonSerializer;
import com.vetti.common.enums.DesensitizedType;
/**
* 数据脱敏注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{
DesensitizedType desensitizedType();
}

View File

@@ -0,0 +1,21 @@
package com.vetti.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author ID
* @date 2025/9/1 15:54
*/
@Component
@ConfigurationProperties(prefix = "here-map") // 配置前缀
@Data
public class HereMapsProperties {
private String apiKey;
private String geocodingApiUrl;
private String reverseGeocodingApiUrl;
private String routerApiUrl;
}

View File

@@ -0,0 +1,41 @@
package com.vetti.common.config;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.http.HttpClient;
import java.time.Duration;
import java.util.concurrent.Executors;
/**
* @author ID
* @date 2025/9/1 16:21
*/
@Configuration
public class HttpClientConfig {
@Value("${http.client.connect-timeout-seconds:10}")
private Integer connectTimeoutSeconds;
/**
* 创建并配置 HttpClient Bean.
* 该实例线程安全,建议在整个应用中复用。
*
* @return 配置好的 HttpClient 实例
*/
@Bean
public HttpClient httpClient() {
return HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // 优先使用 HTTP/2若服务器不支持则降级至 HTTP/1.1
.connectTimeout(Duration.ofSeconds(connectTimeoutSeconds)) // 建立连接的超时时间
.followRedirects(HttpClient.Redirect.NORMAL) // 处理重定向策略 (NORMAL: 同协议重定向)
.executor(Executors.newVirtualThreadPerTaskExecutor()) // 使用虚拟线程执行器Java 21+,可选)
.build();
}
}

View File

@@ -0,0 +1,84 @@
package com.vetti.common.config;
import cn.hutool.core.util.StrUtil;
import com.vetti.common.enums.InternationalLangTypeEnum;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* 国际化配置属性
*
* @author wangxiangshun
*/
@Data
@Component
@ConfigurationProperties(prefix = "vetti.international")
public class InternationalProperties {
/**
* 是否启用默认为False
*/
private boolean enable = true;
/**
* 请求语言参数位置HEADER/PARAM默认为HEADER
*/
private RequestLangParamPosition paramPosition = RequestLangParamPosition.HEADER;
/**
* 请求语言参数名称默认为lang
*/
private String paramName = "lang";
/**
* 获取请求语言
*
* @return 请求语言
*/
public String getLang() {
HttpServletRequest request = null;
try {
request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
} catch (Exception ex) {
// ignore
}
if (null == request) {
return null;
}
String lang;
if (paramPosition == RequestLangParamPosition.HEADER) {
lang = request.getHeader(paramName);
} else {
lang = request.getParameter(paramName);
}
//语言类型特殊转换
if(StrUtil.isNotEmpty(lang)){
lang = lang.replaceAll("-Hans-","_").replaceAll("-Hant-","_").
replaceAll("_Hans_","_").replaceAll("_Hant_","_").replaceAll("-","_");
if(lang.contains("en") || lang.contains("EN")){
lang = InternationalLangTypeEnum.INTERNATIONAL_LANG_TYPE_ENUM_EN.getCode();
}else if(lang.contains("zh") || lang.contains("ZH")){
lang = InternationalLangTypeEnum.INTERNATIONAL_LANG_TYPE_ENUM_ZH.getCode();
}
}else {
lang = InternationalLangTypeEnum.INTERNATIONAL_LANG_TYPE_ENUM_EN.getCode();
}
return lang;
}
/**
* 请求语言参数位置
*/
public enum RequestLangParamPosition {
HEADER, PARAM
}
}

View File

@@ -0,0 +1,46 @@
package com.vetti.common.config;
import io.minio.MinioClient;
import lombok.Data;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Data
@Configuration
public class MinioConfig {
@Value("${fs.minio.endpoint}")
private String endpoint;
@Value("${fs.minio.access-key}")
private String accessKey;
@Value("${fs.minio.secret-key}")
private String secretKey;
// 1. 配置 OkHttp 客户端(设置超时参数)
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(300, TimeUnit.SECONDS) // 连接超时
.readTimeout(600, TimeUnit.SECONDS) // 读取超时
.writeTimeout(600, TimeUnit.SECONDS) // 写入超时
.retryOnConnectionFailure(true) // 连接失败重试
.build();
/**
* 创建 MinIO 客户端实例
*/
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.httpClient(okHttpClient)
.build();
}
}

View File

@@ -0,0 +1,122 @@
package com.vetti.common.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author ruoyi
*/
@Component
@ConfigurationProperties(prefix = "vetti")
public class RuoYiConfig
{
/** 项目名称 */
private String name;
/** 版本 */
private String version;
/** 版权年份 */
private String copyrightYear;
/** 上传路径 */
private static String profile;
/** 获取地址开关 */
private static boolean addressEnabled;
/** 验证码类型 */
private static String captchaType;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public String getCopyrightYear()
{
return copyrightYear;
}
public void setCopyrightYear(String copyrightYear)
{
this.copyrightYear = copyrightYear;
}
public static String getProfile()
{
return profile;
}
public void setProfile(String profile)
{
RuoYiConfig.profile = profile;
}
public static boolean isAddressEnabled()
{
return addressEnabled;
}
public void setAddressEnabled(boolean addressEnabled)
{
RuoYiConfig.addressEnabled = addressEnabled;
}
public static String getCaptchaType() {
return captchaType;
}
public void setCaptchaType(String captchaType) {
RuoYiConfig.captchaType = captchaType;
}
/**
* 获取导入上传路径
*/
public static String getImportPath()
{
return getProfile() + "/import";
}
/**
* 获取头像上传路径
*/
public static String getAvatarPath()
{
return getProfile() + "/avatar";
}
/**
* 获取下载路径
*/
public static String getDownloadPath()
{
return getProfile() + "/download/";
}
/**
* 获取上传路径
*/
public static String getUploadPath()
{
return getProfile() + "/upload";
}
}

View File

@@ -0,0 +1,27 @@
package com.vetti.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author ID
* @date 2025/8/28 14:00
*/
@Data
@Component
@ConfigurationProperties(prefix = "twilio.sendgrid")
public class TwilioConfig {
private String apiKey;
private String fromEmail;
private String fromName;
private TemplateIds templateIds;
@Data
public static class TemplateIds {
private String routezVerificationCode;
}
}

View File

@@ -0,0 +1,17 @@
package com.vetti.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author ID
* @date 2025/8/28 14:01
*/
@Data
@Component
@ConfigurationProperties(prefix = "verification.code.email")
public class VerificationEmailConfig {
private int length;
private int expirationMinutes;
}

View File

@@ -0,0 +1,67 @@
package com.vetti.common.config.serializer;
import java.io.IOException;
import java.util.Objects;
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.vetti.common.annotation.Sensitive;
import com.vetti.common.core.domain.model.LoginUser;
import com.vetti.common.enums.DesensitizedType;
import com.vetti.common.utils.SecurityUtils;
/**
* 数据脱敏序列化过滤
*
* @author ruoyi
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{
private DesensitizedType desensitizedType;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
if (desensitization())
{
gen.writeString(desensitizedType.desensitizer().apply(value));
}
else
{
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.desensitizedType = annotation.desensitizedType();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
/**
* 是否需要脱敏处理
*/
private boolean desensitization()
{
try
{
LoginUser securityUser = SecurityUtils.getLoginUser();
// 管理员不脱敏
return !securityUser.getUser().isAdmin();
}
catch (Exception e)
{
return true;
}
}
}

View File

@@ -0,0 +1,61 @@
package com.vetti.common.constant;
/**
* 缓存的key 常量
*
* @author ruoyi
*/
public class CacheConstants
{
/**
* 登录用户 redis key
*/
public static final String LOGIN_TOKEN_KEY = "login_tokens:";
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
/**
* 防重提交 redis key
*/
public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
/**
* 限流 redis key
*/
public static final String RATE_LIMIT_KEY = "rate_limit:";
/**
* 登录账户密码错误次数 redis key
*/
public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
/**
* 缓存有效期默认720分钟
*/
public final static long EXPIRATION = 7200;
/**
* 缓存刷新时间默认120分钟
*/
public final static long REFRESH_TIME = 120;
/**
* 邮箱验证码 key
*/
public static final String VERIFICATION_EMAIL_CODE_KEY = "verification:email:code:";
}

View File

@@ -0,0 +1,190 @@
package com.vetti.common.constant;
import java.util.Locale;
import io.jsonwebtoken.Claims;
/**
* 通用常量信息
*
* @author ruoyi
*/
public class Constants
{
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* GBK 字符集
*/
public static final String GBK = "GBK";
/**
* 系统语言
*/
public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
/**
* www主域
*/
public static final String WWW = "www.";
/**
* http请求
*/
public static final String HTTP = "http://";
/**
* https请求
*/
public static final String HTTPS = "https://";
/**
* 通用成功标识
*/
public static final String SUCCESS = "0";
/**
* 通用失败标识
*/
public static final String FAIL = "1";
/**
* 登录成功
*/
public static final String LOGIN_SUCCESS = "Success";
/**
* 注销
*/
public static final String LOGOUT = "Logout";
/**
* 注册
*/
public static final String REGISTER = "Register";
/**
* 登录失败
*/
public static final String LOGIN_FAIL = "Error";
/**
* 所有权限标识
*/
public static final String ALL_PERMISSION = "*:*:*";
/**
* 管理员角色权限标识
*/
public static final String SUPER_ADMIN = "admin";
/**
* 角色权限分隔符
*/
public static final String ROLE_DELIMETER = ",";
/**
* 权限标识分隔符
*/
public static final String PERMISSION_DELIMETER = ",";
/**
* 验证码有效期(分钟)
*/
public static final Integer CAPTCHA_EXPIRATION = 2;
/**
* 令牌
*/
public static final String TOKEN = "token";
/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "Bearer ";
/**
* 令牌前缀
*/
public static final String LOGIN_USER_KEY = "login_user_key";
/**
* 用户ID
*/
public static final String JWT_USERID = "userid";
/**
* 用户名称
*/
public static final String JWT_USERNAME = Claims.SUBJECT;
/**
* 用户头像
*/
public static final String JWT_AVATAR = "avatar";
/**
* 创建时间
*/
public static final String JWT_CREATED = "created";
/**
* 用户权限
*/
public static final String JWT_AUTHORITIES = "authorities";
/**
* 资源映射路径 前缀
*/
public static final String RESOURCE_PREFIX = "/profile";
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi:";
/**
* LDAP 远程方法调用
*/
public static final String LOOKUP_LDAP = "ldap:";
/**
* LDAPS 远程方法调用
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 自动识别json对象白名单配置仅允许解析的包名范围越小越安全
*/
public static final String[] JSON_WHITELIST_STR = { "com.vetti" };
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.vetti.quartz.task" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.vetti.common.utils.file", "com.vetti.common.config", "com.vetti.generator" };
/**
* 系统共通常量-0代表未删除
*/
public static final String DEL_FLAG_ZERO = "0";
/**
* 系统共通常量-2代表删除
*/
public static final String DEL_FLAG_ONE = "2";
/**
* 系统业务-国际化语言类型
*/
public static final String SYSTEM_LANGUAGE_TYPE = "zh_CN,en_US";
}

View File

@@ -0,0 +1,117 @@
package com.vetti.common.constant;
/**
* 代码生成通用常量
*
* @author ruoyi
*/
public class GenConstants
{
/** 单表(增删改查) */
public static final String TPL_CRUD = "crud";
/** 树表(增删改查) */
public static final String TPL_TREE = "tree";
/** 主子表(增删改查) */
public static final String TPL_SUB = "sub";
/** 树编码字段 */
public static final String TREE_CODE = "treeCode";
/** 树父编码字段 */
public static final String TREE_PARENT_CODE = "treeParentCode";
/** 树名称字段 */
public static final String TREE_NAME = "treeName";
/** 上级菜单ID字段 */
public static final String PARENT_MENU_ID = "parentMenuId";
/** 上级菜单名称字段 */
public static final String PARENT_MENU_NAME = "parentMenuName";
/** 数据库字符串类型 */
public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };
/** 数据库文本类型 */
public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" };
/** 数据库时间类型 */
public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };
/** 数据库数字类型 */
public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer",
"bit", "bigint", "float", "double", "decimal" };
/** 页面不需要编辑字段 */
public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" };
/** 页面不需要显示的列表字段 */
public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by",
"update_time" };
/** 页面不需要查询字段 */
public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by",
"update_time", "remark" };
/** Entity基类字段 */
public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" };
/** Tree基类字段 */
public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" };
/** 文本框 */
public static final String HTML_INPUT = "input";
/** 文本域 */
public static final String HTML_TEXTAREA = "textarea";
/** 下拉框 */
public static final String HTML_SELECT = "select";
/** 单选框 */
public static final String HTML_RADIO = "radio";
/** 复选框 */
public static final String HTML_CHECKBOX = "checkbox";
/** 日期控件 */
public static final String HTML_DATETIME = "datetime";
/** 图片上传控件 */
public static final String HTML_IMAGE_UPLOAD = "imageUpload";
/** 文件上传控件 */
public static final String HTML_FILE_UPLOAD = "fileUpload";
/** 富文本控件 */
public static final String HTML_EDITOR = "editor";
/** 字符串类型 */
public static final String TYPE_STRING = "String";
/** 整型 */
public static final String TYPE_INTEGER = "Integer";
/** 长整型 */
public static final String TYPE_LONG = "Long";
/** 浮点型 */
public static final String TYPE_DOUBLE = "Double";
/** 高精度计算类型 */
public static final String TYPE_BIGDECIMAL = "BigDecimal";
/** 时间类型 */
public static final String TYPE_DATE = "Date";
/** 模糊查询 */
public static final String QUERY_LIKE = "LIKE";
/** 相等查询 */
public static final String QUERY_EQ = "EQ";
/** 需要 */
public static final String REQUIRE = "1";
}

View File

@@ -0,0 +1,94 @@
package com.vetti.common.constant;
/**
* 返回状态码
*
* @author ruoyi
*/
public class HttpStatus
{
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 对象创建成功
*/
public static final int CREATED = 201;
/**
* 请求已经被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已经执行成功,但是没有返回数据
*/
public static final int NO_CONTENT = 204;
/**
* 资源已被移除
*/
public static final int MOVED_PERM = 301;
/**
* 重定向
*/
public static final int SEE_OTHER = 303;
/**
* 资源没有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 参数列表错误(缺少,格式不匹配)
*/
public static final int BAD_REQUEST = 400;
/**
* 未授权
*/
public static final int UNAUTHORIZED = 401;
/**
* 访问受限,授权过期
*/
public static final int FORBIDDEN = 403;
/**
* 资源,服务未找到
*/
public static final int NOT_FOUND = 404;
/**
* 不允许的http方法
*/
public static final int BAD_METHOD = 405;
/**
* 资源冲突,或者资源被锁
*/
public static final int CONFLICT = 409;
/**
* 不支持的数据,媒体类型
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
* 系统内部错误
*/
public static final int ERROR = 500;
/**
* 接口未实现
*/
public static final int NOT_IMPLEMENTED = 501;
/**
* 系统警告消息
*/
public static final int WARN = 601;
}

View File

@@ -0,0 +1,64 @@
package com.vetti.common.constant;
/**
* ClassName: LoginConstant
* Description:
* Datetime: 2025/08/30 14:05
* Author: wangxiangshun
*/
public class LoginConstant {
/**
* 令牌前缀
*/
public static final String LOGIN_USER_KEY = "login_user_key";
public final static String LOGIN_IN_LOCK = "login:lock:";
public final static String LOGIN_IN_CODE = "login:code:";
public final static Integer LOGIN_IN_ATTEMPTS = 10;
public final static Integer SEND_CODE_ATTEMPTS = 15;
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "hamkke_codes:";
/**
* 验证码 redis key
*/
public static final String ONEKEY_KEY = "hamkke_onekey:";
/**
* 取手机验证码的key值的前缀
*/
public static final String VER_CODE_PREFIX = "ver_:";
/**
* 短信 验证码有效期(分钟)
*/
public static final Long SMS_CAPTCHA_EXPIRATION = 5L;
public final static String IOS = "IOS";
public final static String ANDROID = "Android";
public final static String APP = "App";
public final static String MINIAPP = "miniApp";
public static final String WX = "wx";
public static final String CODE = "code";
public static final String PHONE = "phone";
/**
* 令牌前缀
*/
public static final String LOGIN_USER_KEY_MINIAPP = "login_user:mini:";
public static final String LOGIN_USER_KEY_APP = "login_user:app:";
}

View File

@@ -0,0 +1,50 @@
package com.vetti.common.constant;
/**
* 任务调度通用常量
*
* @author ruoyi
*/
public class ScheduleConstants
{
public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
/** 执行目标key */
public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
/** 默认 */
public static final String MISFIRE_DEFAULT = "0";
/** 立即触发执行 */
public static final String MISFIRE_IGNORE_MISFIRES = "1";
/** 触发一次执行 */
public static final String MISFIRE_FIRE_AND_PROCEED = "2";
/** 不触发立即执行 */
public static final String MISFIRE_DO_NOTHING = "3";
public enum Status
{
/**
* 正常
*/
NORMAL("0"),
/**
* 暂停
*/
PAUSE("1");
private String value;
private Status(String value)
{
this.value = value;
}
public String getValue()
{
return value;
}
}
}

View File

@@ -0,0 +1,49 @@
package com.vetti.common.constant;
/**
* 权限相关通用常量
*
* @author hamkke
*/
public class SecurityConstants
{
/**
* 用户ID字段
*/
public static final String DETAILS_USER_ID = "user_id";
/**
* 用户名字段
*/
public static final String DETAILS_USERNAME = "username";
/**
* 授权信息字段
*/
public static final String AUTHORIZATION_HEADER = "Authorization";
/**
* 请求来源
*/
public static final String FROM_SOURCE = "from-source";
/**
* 内部请求
*/
public static final String INNER = "inner";
/**
* 用户标识
*/
public static final String USER_KEY = "user_key";
/**
* 登录用户
*/
public static final String LOGIN_USER = "login_user";
/**
* 角色权限
*/
public static final String ROLE_PERMISSION = "role_permission";
}

View File

@@ -0,0 +1,18 @@
package com.vetti.common.constant;
/**
* SysDictData 常量
*/
public class SysDictDataConstants {
/**
* 任务分配情况-已分配
*/
public static final String COMMAND_TASK_TASK_ALLOCATE_KEY = "assigned";//
/**
* 任务分配情况-未分配
*/
public static final String COMMAND_TASK_TASK_UNALLOCATE_KEY = "unassigned";
}

View File

@@ -0,0 +1,20 @@
package com.vetti.common.constant;
/**
* Token的Key常量
*
* @author wangxiangshun
*/
public class TokenConstants
{
/**
* 令牌前缀
*/
public static final String PREFIX = "Bearer ";
/**
* 令牌秘钥
*/
public final static String SECRET = "abcdefghijklmnopqrstuvwxyz";
}

View File

@@ -0,0 +1,81 @@
package com.vetti.common.constant;
/**
* 用户常量信息
*
* @author ruoyi
*/
public class UserConstants
{
/**
* 平台内系统用户的唯一标志
*/
public static final String SYS_USER = "SYS_USER";
/** 正常状态 */
public static final String NORMAL = "0";
/** 异常状态 */
public static final String EXCEPTION = "1";
/** 用户封禁状态 */
public static final String USER_DISABLE = "1";
/** 角色正常状态 */
public static final String ROLE_NORMAL = "0";
/** 角色封禁状态 */
public static final String ROLE_DISABLE = "1";
/** 部门正常状态 */
public static final String DEPT_NORMAL = "0";
/** 部门停用状态 */
public static final String DEPT_DISABLE = "1";
/** 字典正常状态 */
public static final String DICT_NORMAL = "0";
/** 是否为系统默认(是) */
public static final String YES = "Y";
/** 是否菜单外链(是) */
public static final String YES_FRAME = "0";
/** 是否菜单外链(否) */
public static final String NO_FRAME = "1";
/** 菜单类型(目录) */
public static final String TYPE_DIR = "M";
/** 菜单类型(菜单) */
public static final String TYPE_MENU = "C";
/** 菜单类型(按钮) */
public static final String TYPE_BUTTON = "F";
/** Layout组件标识 */
public final static String LAYOUT = "Layout";
/** ParentView组件标识 */
public final static String PARENT_VIEW = "ParentView";
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
/** 校验是否唯一的返回标识 */
public final static boolean UNIQUE = true;
public final static boolean NOT_UNIQUE = false;
/**
* 用户名长度限制
*/
public static final int USERNAME_MIN_LENGTH = 2;
public static final int USERNAME_MAX_LENGTH = 20;
/**
* 密码长度限制
*/
public static final int PASSWORD_MIN_LENGTH = 5;
public static final int PASSWORD_MAX_LENGTH = 20;
}

View File

@@ -0,0 +1,221 @@
package com.vetti.common.core.controller;
import java.beans.PropertyEditorSupport;
import java.util.Date;
import java.util.List;
import com.vetti.common.core.page.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.vetti.common.constant.HttpStatus;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.model.LoginUser;
import com.vetti.common.utils.DateUtils;
import com.vetti.common.utils.PageUtils;
import com.vetti.common.utils.SecurityUtils;
import com.vetti.common.utils.StringUtils;
import com.vetti.common.utils.sql.SqlUtil;
/**
* web层通用数据处理
*
* @author ruoyi
*/
public class BaseController
{
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 将前台传递过来的日期格式的字符串自动转化为Date类型
*/
@InitBinder
public void initBinder(WebDataBinder binder)
{
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
{
@Override
public void setAsText(String text)
{
setValue(DateUtils.parseDate(text));
}
});
}
/**
* 设置请求分页数据
*/
protected void startPage()
{
PageUtils.startPage();
}
/**
* 设置请求排序数据
*/
protected void startOrderBy()
{
PageDomain pageDomain = TableSupport.buildPageRequest();
if (StringUtils.isNotEmpty(pageDomain.getOrderBy()))
{
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
PageHelper.orderBy(orderBy);
}
}
/**
* 清理分页的线程变量
*/
protected void clearPage()
{
PageUtils.clearPage();
}
/**
* 响应请求分页数据
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected TableDataInfo getDataTable(List<?> list)
{
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS);
rspData.setMsg("查询成功");
rspData.setRows(list);
rspData.setTotal(new PageInfo(list).getTotal());
return rspData;
}
/**
* 返回成功
*/
public AjaxResult success()
{
return AjaxResult.success();
}
/**
* 返回失败消息
*/
public AjaxResult error()
{
return AjaxResult.error();
}
/**
* 返回成功消息
*/
public AjaxResult success(String message)
{
return AjaxResult.success(message);
}
/**
* 返回成功消息
*/
public AjaxResult success(Object data)
{
return AjaxResult.success(data);
}
/**
* 返回失败消息
*/
public AjaxResult error(String message)
{
return AjaxResult.error(message);
}
/**
* 返回警告消息
*/
public AjaxResult warn(String message)
{
return AjaxResult.warn(message);
}
/**
* 响应返回结果
*
* @param rows 影响行数
* @return 操作结果
*/
protected AjaxResult toAjax(int rows)
{
return rows > 0 ? AjaxResult.success() : AjaxResult.error();
}
/**
* 响应返回结果
*
* @param result 结果
* @return 操作结果
*/
protected AjaxResult toAjax(boolean result)
{
return result ? success() : error();
}
/**
* 页面跳转
*/
public String redirect(String url)
{
return StringUtils.format("redirect:{}", url);
}
/**
* 获取用户缓存信息
*/
public LoginUser getLoginUser()
{
return SecurityUtils.getLoginUser();
}
/**
* 获取登录用户id
*/
public Long getUserId()
{
return getLoginUser().getUserId();
}
/**
* 获取登录部门id
*/
public Long getDeptId()
{
return getLoginUser().getDeptId();
}
/**
* 获取登录用户名
*/
public String getUsername()
{
return getLoginUser().getUsername();
}
/**
* 响应请求分页数据
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected <T> TablePageDataInfo<T> getPageDataTable(List<T> list)
{
TablePageDataInfo<T> pageDataInfo = new TablePageDataInfo<T>();
TableAppDataInfo<T> rspData = new TableAppDataInfo<T>();
rspData.setData(list);
rspData.setTotal(new PageInfo(list).getTotal());
rspData.setPages(new PageInfo(list).getPages());
rspData.setPageNum(new PageInfo(list).getPageNum());
rspData.setHasNextPage(new PageInfo(list).isHasNextPage());
pageDataInfo.setData(rspData);
pageDataInfo.setCode(HttpStatus.SUCCESS);
return pageDataInfo;
}
}

View File

@@ -0,0 +1,216 @@
package com.vetti.common.core.domain;
import java.util.HashMap;
import java.util.Objects;
import com.vetti.common.constant.HttpStatus;
import com.vetti.common.utils.StringUtils;
/**
* 操作消息提醒
*
* @author ruoyi
*/
public class AjaxResult<T> extends HashMap<String, Object>
{
private static final long serialVersionUID = 1L;
/** 状态码 */
public static final String CODE_TAG = "code";
/** 返回内容 */
public static final String MSG_TAG = "msg";
/** 数据对象 */
public static final String DATA_TAG = "data";
/**
* 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
*/
public AjaxResult()
{
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
*/
public AjaxResult(int code, String msg)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
*/
public AjaxResult(int code, String msg, Object data)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (StringUtils.isNotNull(data))
{
super.put(DATA_TAG, data);
}
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success()
{
return AjaxResult.success("操作成功");
}
/**
* 返回成功数据
*
* @return 成功消息
*/
public static AjaxResult success(Object data)
{
return AjaxResult.success("操作成功", data);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @return 成功消息
*/
public static AjaxResult success(String msg)
{
return AjaxResult.success(msg, null);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/
public static AjaxResult success(String msg, Object data)
{
return new AjaxResult(HttpStatus.SUCCESS, msg, data);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult warn(String msg)
{
return AjaxResult.warn(msg, null);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data)
{
return new AjaxResult(HttpStatus.WARN, msg, data);
}
/**
* 返回错误消息
*
* @return 错误消息
*/
public static AjaxResult error()
{
return AjaxResult.error("操作失败");
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @return 错误消息
*/
public static AjaxResult error(String msg)
{
return AjaxResult.error(msg, null);
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 错误消息
*/
public static AjaxResult error(String msg, Object data)
{
return new AjaxResult(HttpStatus.ERROR, msg, data);
}
/**
* 返回错误消息
*
* @param code 状态码
* @param msg 返回内容
* @return 错误消息
*/
public static AjaxResult error(int code, String msg)
{
return new AjaxResult(code, msg, null);
}
/**
* 是否为成功消息
*
* @return 结果
*/
public boolean isSuccess()
{
return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG));
}
/**
* 是否为警告消息
*
* @return 结果
*/
public boolean isWarn()
{
return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG));
}
/**
* 是否为错误消息
*
* @return 结果
*/
public boolean isError()
{
return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG));
}
/**
* 方便链式调用
*
* @param key 键
* @param value 值
* @return 数据对象
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
}

View File

@@ -0,0 +1,159 @@
package com.vetti.common.core.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.vetti.common.utils.SecurityUtils;
/**
* Entity基类
*
* @author ruoyi
*/
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 搜索值
*/
@JsonIgnore
private String searchValue;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private String updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 备注
*/
private String remark;
/**
* 删除标志0代表存在 2代表删除
*/
private String delFlag;
/**
* 版本号
*/
private Long versionNo;
/**
* 请求参数
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params;
public String getSearchValue() {
return searchValue;
}
public void setSearchValue(String searchValue) {
this.searchValue = searchValue;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getDelFlag() {
return delFlag;
}
public void setDelFlag(String delFlag) {
this.delFlag = delFlag;
}
public Long getVersionNo() {
return versionNo;
}
public void setVersionNo(Long versionNo) {
this.versionNo = versionNo;
}
public Map<String, Object> getParams() {
if (params == null) {
params = new HashMap<>();
}
return params;
}
public void setParams(Map<String, Object> params) {
this.params = params;
}
public void setCreate() {
this.setCreateBy(SecurityUtils.getLoginUser().getUsername());
this.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
this.setCreateTime(new Date());
this.setUpdateTime(new Date());
this.setDelFlag("0");
}
public void setUpdate() {
this.setUpdateBy("system");
this.setUpdateTime(new Date());
}
}

View File

@@ -0,0 +1,46 @@
package com.vetti.common.core.domain;
import lombok.Data;
import java.time.ZonedDateTime;
import java.util.Map;
/**
* 文件信息DTO类用于封装文件的详细信息
*
* @author ID
* @date 2025/8/26 22:57
*/
@Data
public class MinioFileInfo {
/**
* 文件名称(包含路径)
*/
private String fileName;
/**
* 文件大小(单位:字节)
*/
private long size;
/**
* 最后修改时间
*/
private ZonedDateTime lastModified;
/**
* 文件内容类型MIME类型
*/
private String contentType;
/**
* 是否为目录
*/
private boolean isDirectory;
/**
* 文件元数据信息
*/
private Map<String, String> metadata;
}

View File

@@ -0,0 +1,116 @@
package com.vetti.common.core.domain;
import java.io.Serializable;
import com.vetti.common.constant.HttpStatus;
import com.vetti.common.utils.MessageUtils;
/**
* 响应信息主体
*
* @author ruoyi
*/
public class R<T> implements Serializable
{
private static final long serialVersionUID = 1L;
/** 成功 */
public static final int SUCCESS = HttpStatus.SUCCESS;
/** 失败 */
public static final int FAIL = HttpStatus.ERROR;
private int code;
private String msg;
private T data;
public static <T> R<T> ok()
{
return restResult(null, SUCCESS, MessageUtils.messageCustomize("systemR10001"));
}
public static <T> R<T> ok(T data)
{
return restResult(data, SUCCESS, MessageUtils.messageCustomize("systemR10001"));
}
public static <T> R<T> ok(T data, String msg)
{
return restResult(data, SUCCESS, msg);
}
public static <T> R<T> fail()
{
return restResult(null, FAIL, MessageUtils.messageCustomize("systemR10001"));
}
public static <T> R<T> fail(String msg)
{
return restResult(null, FAIL, msg);
}
public static <T> R<T> fail(T data)
{
return restResult(data, FAIL, MessageUtils.messageCustomize("systemR10001"));
}
public static <T> R<T> fail(T data, String msg)
{
return restResult(data, FAIL, msg);
}
public static <T> R<T> fail(int code, String msg)
{
return restResult(null, code, msg);
}
private static <T> R<T> restResult(T data, int code, String msg)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
public T getData()
{
return data;
}
public void setData(T data)
{
this.data = data;
}
public static <T> Boolean isError(R<T> ret)
{
return !isSuccess(ret);
}
public static <T> Boolean isSuccess(R<T> ret)
{
return R.SUCCESS == ret.getCode();
}
}

Some files were not shown because too many files have changed in this diff Show More