Solon + EasyQuery + ElementPlus 实现后台管理系统之 02-数据库设计与项目结构搭建
AI 摘要
前置工作
开发环境
项目开发使用的开发环境及相关版本为 IntelliJ IDEA、JDK 1.8、MySQL 8.0.42、Redis 7.2.6、Solon 3.7.2、EasyQuery 3.1.62、Redisx 1.7.0、HuTool 5.8.41、SaToken 1.44.0、Vue 3.5.25、ElementPlus 2.11.9。
数据库结构
根据通用后台管理系统的业务需求设计数据库结构,并初始化测试数据。本文提供的数据库结构及数据库脚本仅供参考,推荐根据实际需求自行扩展。
部门表(dept):
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | int | 部门 ID |
| name | varchar | 部门名称 |
| deleted | bigint | 删除状态,0 未删除 该字段用于逻辑删除 |
角色表(role):
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | int | 角色 ID |
| name | varchar | 角色名称 |
| perms | text | 授权的权限菜单列表 |
| remarks | varchar | 角色备注 |
| deleted | bigint | 删除状态,0 未删除 该字段用于逻辑删除 |
权限表(perms):
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | int | 权限 ID |
| parent_id | int | 父级权限 ID |
| name | varchar | 权限名称 |
| identifier | varchar | 权限标识 |
| path | int | 权限路由路径 |
| component | varchar | 权限路由组件 |
| type | int | 权限类型,0 目录/1 菜单/2 操作 |
| sort_id | int | 排序 ID |
| status | int | 权限状态,1 显示/0 隐藏 |
| deleted | bigint | 删除状态,0 未删除 该字段用于逻辑删除 |
用户表(users):
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | int | 用户 ID |
| dept_id | int | 关联部门 ID |
| role_id | int | 关联角色 ID |
| username | varchar | 账户账号 |
| password | varchar | 账户密码 |
| salts | varchar | 账户盐值 |
| realname | varchar | 真实姓名 |
| avatar | varchar | 账户头像 |
| remarks | varchar | 用户备注 |
| status | int | 账户状态,1 正常/0 禁用 |
| deleted | bigint | 删除状态,0 未删除 该字段用于逻辑删除 |
登录日志表(loginlog):
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | int | 日志 ID |
| user_id | int | 关联用户 ID |
| ip | varchar | 登录 IP 信息 |
| address | varchar | 登录 IP 地址 |
| timestamp | bigint | 登录时间戳 |
操作日志表(actionlog):
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | int | 日志 ID |
| user_id | int | 关联用户 ID |
| ip | varchar | 操作 IP 信息 |
| address | varchar | 操作 IP 地址 |
| timestamp | bigint | 操作时间戳 |
| title | varchar | 日志标题 |
| request_url | varchar | 请求地址 |
| request_method | varchar | 请求方式 |
| request_params | text | 请求参数 |
| response_result | text | 响应结果 |
附数据库脚本:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for actionlog
-- ----------------------------
DROP TABLE IF EXISTS `actionlog`;
CREATE TABLE `actionlog` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '日志ID',
`user_id` int NOT NULL COMMENT '关联用户ID',
`ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作IP信息',
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作IP地址',
`timestamp` bigint NOT NULL COMMENT '操作时间戳',
`title` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '日志标题',
`request_url` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求地址',
`request_method` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求方式',
`request_params` text COLLATE utf8mb4_general_ci COMMENT '请求参数',
`response_result` text COLLATE utf8mb4_general_ci COMMENT '响应结果',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='操作日志表';
-- ----------------------------
-- Records of actionlog
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '部门ID',
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '部门名称',
`deleted` bigint NOT NULL DEFAULT '0' COMMENT '删除状态,0未删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='部门表';
-- ----------------------------
-- Records of dept
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for loginlog
-- ----------------------------
DROP TABLE IF EXISTS `loginlog`;
CREATE TABLE `loginlog` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '日志ID',
`user_id` int NOT NULL COMMENT '关联用户ID',
`ip` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录IP信息',
`address` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录IP地址',
`timestamp` bigint NOT NULL COMMENT '登录时间戳',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='登录日志表';
-- ----------------------------
-- Records of loginlog
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for perms
-- ----------------------------
DROP TABLE IF EXISTS `perms`;
CREATE TABLE `perms` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '权限ID',
`parent_id` int NOT NULL COMMENT '父级权限ID',
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限名称',
`identifier` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限标识',
`path` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '权限路由路径',
`component` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '权限路由组件',
`type` int NOT NULL COMMENT '权限类型,0目录/1菜单/2操作',
`sort_id` int NOT NULL DEFAULT '0' COMMENT '排序ID',
`status` int NOT NULL DEFAULT '1' COMMENT '权限状态,1显示/0隐藏',
`deleted` bigint NOT NULL DEFAULT '0' COMMENT '删除状态,0未删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='权限表';
-- ----------------------------
-- Records of perms
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色名称',
`perms` text COLLATE utf8mb4_general_ci COMMENT '授权的权限菜单列表',
`remarks` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '-' COMMENT '角色备注',
`deleted` bigint NOT NULL DEFAULT '0' COMMENT '删除状态,0未删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色表';
-- ----------------------------
-- Records of role
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`dept_id` int NOT NULL COMMENT '关联部门ID',
`role_id` int NOT NULL COMMENT '关联角色ID',
`username` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '账户账号',
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '账户密码',
`salts` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '账户盐值',
`realname` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '真实姓名',
`avatar` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '账户头像',
`remarks` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户备注',
`status` int NOT NULL COMMENT '账户状态,1正常/0禁用',
`deleted` bigint NOT NULL DEFAULT '0' COMMENT '删除状态,0未删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
-- ----------------------------
-- Records of users
-- ----------------------------
BEGIN;
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;构建后端项目
Solon Web
在 IntelliJ IDEA 中创建 Solon 3.7.2 项目,移除多余的控制器、样式文件、视图文件。
编写 Maven 项目配置文件(pom.xml),保留依赖:
<!-- Solon Web -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-web</artifactId>
</dependency>
<!-- Solon Logback -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-logging-logback</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Solon Test -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-test</artifactId>
<scope>test</scope>
</dependency>编写 Solon 项目配置文件(classpath:/app.yml),保留配置:
server:
port: 8080 # 后端API端口号
solon:
logging:
appender:
console: # 控制台输出
level: INFO # 输出级别为INFO的日志
file: # 文件输出
level: INFO # 输出级别为INFO的日志MySQL
编写 Maven 项目配置文件(pom.xml),加入 HikariCP 数据源依赖、MySQL 驱动依赖:
<!-- HikariCP -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.2.0</version>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>编写 Solon 项目配置文件(classpath:/app.yml),配置数据源:
solon:
# ...
dataSources:
ds1!: # 数据源-ds1
class: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/sadmin?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456..EasyQuery
EasyQuery 是一个国产开源 ORM 框架,是 Java 下最强的、最懂查询的 ORM。
EasyQuery 文档:https://www.easy-query.com/
参考文档:https://solon.noear.org/article/579
编写 Maven 项目配置文件(pom.xml),加入 EasyQuery 依赖:
<!-- EasyQuery -->
<dependency>
<groupId>com.easy-query</groupId>
<artifactId>sql-solon-plugin</artifactId>
<version>3.1.62</version>
</dependency>
<dependency>
<groupId>com.easy-query</groupId>
<artifactId>sql-processor</artifactId>
<version>3.1.62</version>
</dependency>编写 Solon 项目配置文件(classpath:/app.yml),配置 EasyQuery:
easy-query:
ds1: # 数据源-ds1
database: mysql # 数据库类型设置为mysql
print-sql: true # 打印SQL日志Redis
Redis 作为高性能的内存数据库,核心用于缓存提速、并发控制、数据暂存、异步通信等场景,尤其适合解决业务系统中高频查询、高并发操作、临时数据处理的痛点。
在 Solon 项目中操作 Redis,推荐使用 Redisx 客户端。
Redisx 是一个轻量级的 Redis Client,基于 Jedis 进行的友好封装。
Redisx 文档:https://gitee.com/noear/redisx
编写 Maven 项目配置文件(pom.xml),加入 Redisx 依赖:
<!-- Redisx -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>redisx</artifactId>
<version>1.7.0</version>
</dependency>编写 Solon 项目配置文件(classpath:/app.yml),配置 Redisx:
redis:
rd1: # 数据源-ds1
server: localhost:6379 # Redis服务地址
db: 1 # Redis数据库编号Redisx 中的操作客户端 RedisClient Bean 需要手动构建。
编写 Redisx 配置类(cn.duozai.sadmin.config.RedisxConfig):
/**
* Redisx 配置类
* @visduo
*/
@Configuration // 标记配置类
public class RedisxConfig {
/**
* 构建RedisClient Bean
* @visduo
*
* @Bean:构建一个Bean对象
* @Inject:将redis.rd1配置信息注入给RedisClient
*
* @param client 注入RedisClient
* @return RedisClient
*/
@Bean
public RedisClient redisClient(@Inject("${redis.rd1}") RedisClient client) {
return client;
}
}SaToken
SaToken 是一款开源、免费、轻量级的 Java 权限认证框架,让鉴权变得简单、优雅。
SaToken 文档:https://sa-token.cc/
参考文档:https://solon.noear.org/article/110
编写 Maven 项目配置文件(pom.xml),加入 SaToken 依赖、SaToken-Redisx 依赖、SaToken-Snack3 依赖:
<!-- SaToken-Solon -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-solon-plugin</artifactId>
<version>1.44.0</version>
</dependency>
<!-- SaToken-Redisx -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redisx</artifactId>
<version>1.44.0</version>
</dependency>
<!-- SaToken-Snack3 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-snack3</artifactId>
<version>1.44.0</version>
</dependency>编写 Solon 项目配置文件(classpath:/app.yml),配置 SaToken:
sa-token:
token-name: authtoken # Token名称
timeout: 2592000 # Token有效期
is-concurrent: false # 是否允许同一账号并发登录
token-style: uuid # Token风格
is-log: true # 打印日志编写 SaToken 配置类(cn.duozai.sadmin.config.SaTokenConfig):
/**
* SaToken配置类
* @visduo
*/
@Configuration
public class SaTokenConfig {
/**
* 构建SaTokenDao Bean
* @visduo
*
* @Inject:注入RedisClient
* 多种 Redis 接口适配可以复用一份配置
* 参考文档:https://solon.noear.org/article/592
*
* @param client 注入RedisClient
* @return SaTokenDao
*/
@Bean
public SaTokenDao saTokenDao(@Inject RedisClient client){
return new SaTokenDaoForRedisx(client);
}
}HuTool
Hutool 是一个功能丰富且易用的 Java 工具库,封装的工具涵盖了字符串、数字、集合、编码、日期、文件、IO、加密、数据库 JDBC、JSON、HTTP 客户端等一系列操作。
HuTool 文档:https://www.hutool.cn/
编写 Maven 项目配置文件(pom.xml),加入 HuTool 依赖:
<!-- HuTool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.41</version>
</dependency>ip2region
ip2region 是一个离线 IP 地址定位库和 IP 定位数据管理框架,同时支持 IPv4 和 IPv6,10 微秒级别的查询效率,提供了众多主流编程语言的 xdb 数据生成和查询客户端实现。
ip2region 文档:https://gitee.com/lionsoul/ip2region
编写 Maven 项目配置文件(pom.xml),加入 ip2region 依赖:
<!-- ip2region -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>3.1.1</version>
</dependency>下载 ip2region_v4.xdb 数据库文件,放至 classpath:/db 目录下:
编写 ip2region 工具类(cn.duozai.sadmin.utils.ClientipUtil):
/**
* 客户端IP地址工具类
* @visduo
*/
public class ClientipUtil {
/**
* 日志记录器
*/
private static final Logger logger = LoggerFactory.getLogger(ClientipUtil.class);
/**
* ip2region数据库文件路径
*
* ResourceUtil.getResource().getPath():Solon内置工具类提供的方法,用于获取classpath下的文件的具体路径
*/
private static final String dbPath = ResourceUtil.getResource("db/ip2region_v4.xdb").getPath();
/**
* ip2region版本,设定为IPv4
*/
private static final Version version = Version.IPv4;
/**
* 解析IP地址
* @visduo
*
* @param ip IP信息
* @return IP地址
*/
public static String parse(String ip) {
// 0、特殊IP处理
if(ip.contains("localhost")) {
// localhost直接返回内网IP
return "内网IP";
} else if(!Validator.isIpv4(ip)) {
// 判断IPv4地址是否合法,不合法则结束
// 参考文档:https://plus.hutool.cn/apidocs/cn/hutool/core/lang/Validator.html
return "未知";
} else if(Ipv4Util.isInnerIP(ip)) {
// 其他格式的内网IP由ip2region处理结果比较乱,可以调用HuTool工具类判断是否为内网IP
// 参考文档:https://plus.hutool.cn/apidocs/cn/hutool/core/net/Ipv4Util.html
return "内网IP";
}
// 参考文档:参考文档:https://gitee.com/lionsoul/ip2region/tree/master/binding/java
// 1、文件验证
try {
Searcher.verifyFromFile(dbPath);
} catch (Exception e) {
// 适用性验证失败
// 当前查询客户端实现不适用于dbPath指定的xdb文件的查询.
// 应该停止启动服务,使用合适的xdb文件或者升级到适合dbPath的Searcher实现
logger.error("ip2region数据库文件验证失败:{}", e.getMessage());
return "未知";
}
// 2、从dbPath加载整个xdb到内存
LongByteArray cBuff;
try {
cBuff = Searcher.loadContentFromFile(dbPath);
} catch (Exception e) {
logger.error("ip2region数据库文件加载失败:{}", e.getMessage());
return "未知";
}
// 3、使用上述的cBuff创建一个完全基于内存的查询对象
Searcher searcher;
try {
searcher = Searcher.newWithBuffer(version, cBuff);
} catch (Exception e) {
logger.error("ip2region创建查询对象失败:{}", e.getMessage());
return "未知";
}
// 4、查询
try {
String searchResult = searcher.search(ip);
// 查询结果格式:中国|0|福建省|厦门市|电信
// 注意查询结果格式:不同版本的xdb格式可能不同,自行输出searchResult后,根据实际结果格式进行格式化处理
// 只需要提取省市县运营商,格式化字符串提取
String[] split = searchResult.split("\\|");
return split[2] + split[3] + split[4];
} catch (Exception e) {
logger.error("ip2region查询失败:{}", e.getMessage());
}
// 5、关闭资源:该searcher对象可以安全用于并发,等整个服务关闭的时候再关闭searcher
// searcher.close();
return "未知";
}
/**
* 测试方法
* @visduo
*
* @param args main args
*/
public static void main(String[] args) {
String ip1 = "192.168.3.1";
String ip2 = "localhost";
String ip3 = "27.154.86.48";
String ip4 = "211.99.98.197";
String ip5 = "112.5.16.1";
logger.debug("{}归属地为:{}", ip1, parse(ip1));
logger.debug("{}归属地为:{}", ip2, parse(ip2));
logger.debug("{}归属地为:{}", ip3, parse(ip3));
logger.debug("{}归属地为:{}", ip4, parse(ip4));
logger.debug("{}归属地为:{}", ip5, parse(ip5));
}
}测试结果:
ip2region同时支持 IPv4 和 IPv6 的归属地查询,示例代码仅实现 IPv4 归属地查询,IPv6 归属地查询自行优化适配。
MD5Salts
MD5Salts(MD5 加盐)是为增强 MD5 算法在密码存储场景中的安全性而设计的一种技术手段,核心是通过引入随机的盐值字符串,解决 MD5 因为相同输入对应相同哈希值而易受彩虹表攻击、暴力破解的问题。
盐值是一段随机生成的字符串,在密码 MD5 哈希时,将盐值与原始密码拼接后再进行 MD5 计算,即使两个用户密码相同,由于盐值不同,最终的哈希值也会完全不同,从而大幅提升破解难度。
编写 MD5 盐值加密工具类(cn.duozai.sadmin.utils.MD5SaltsUtil):
/**
* MD5盐值加密工具类
* @visduo
*/
public class MD5SaltsUtil {
/**
* 日志记录器
*/
private static final Logger logger = LoggerFactory.getLogger(MD5SaltsUtil.class);
/**
* 生成随机盐值
* @visduo
*
* @return 随机盐值
*/
public static String salts() {
// 调用HuTool工具类生成随机16位长度的字符串
// 参考文档:https://plus.hutool.cn/apidocs/cn/hutool/core/util/RandomUtil.html#randomString-int-
return RandomUtil.randomString(16);
}
/**
* MD5盐值加密
* @visduo
*
* @param password 明文密码
* @param salts 盐值
* @return 密文密码
*/
public static String md5(String password, String salts) {
// 密码和盐值进行MD5加密
// 参考文档:https://plus.hutool.cn/apidocs/cn/hutool/crypto/SecureUtil.html#md5--
// 在明文密码的基础上进行撒盐的规则可自定义
return SecureUtil.md5(salts + password + salts + salts);
}
/**
* 测试方法
* @visduo
*
* @param args main args
*/
public static void main(String[] args) {
// 明文密码
String password = "123456";
// 普通MD5加密密文密码
String md5Password = SecureUtil.md5(password);
// 加盐MD5加密密文密码
String md5SaltsPassword = md5(password, salts());
logger.debug("密码{}的普通MD5加密密文密码为:{}", password, md5Password);
logger.debug("密码{}的加盐MD5加密密文密码为:{}", password, md5SaltsPassword);
}
}测试结果:
ResponseResult
ResponseResult 是业务上自定义的统一响应结果类,核心作用是为了规范接口返回格式,统一前后端数据交互标准。后端所有的接口不再零散返回字符串、实体类等数据,而是统一包装成 ResponseResult 对象,让前端能以固定格式解析结果。
一个标准的 ResponseResult 通常包含 3 个核心字段:
| 名称 | 描述 |
|---|---|
| code | 响应状态,通常是 int/枚举类型 如 200/成功、-1/失败、500/系统异常、400/参数错误、401/未登录 |
| message | 响应信息,通常是 String 类型 如成功时返回操作成功,失败时返回具体错误提示 |
| data | 响应数据,通常是 Object/范型 一般返回业务数据,成功时返回具体数据(如列表数据),失败时返回 null |
编写统一响应结果类(cn.duozai.sadmin.utils.ResponseResult):
/**
* 统一响应结果类
* @visduo
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseResult {
/**
* 响应码
* 200成功/-1失败/500异常/401未登录/403无权限/404不存在/400参数错误
*/
public Integer code;
/**
* 响应信息
*/
public String message;
/**
* 响应数据
*/
public Object data;
/**
* 响应成功
* @visduo
*
* @param message 响应信息
* @param data 响应数据
* @return 响应结果实体
*/
public static ResponseResult success(String message, Object data) {
return new ResponseResult(200, message, data);
}
/**
* 响应失败
* @visduo
*
* @param message 响应信息
* @param data 响应数据
* @return 响应结果实体
*/
public static ResponseResult failure(String message, Object data) {
return new ResponseResult(-1, message, data);
}
/**
* 响应异常
* @visduo
*
* @param message 响应信息
* @param data 响应数据
* @return 响应结果实体
*/
public static ResponseResult error(String message, Object data) {
return new ResponseResult(500, message, data);
}
}Entity
使用 EasyQueryAssistant 插件生成数据库表实体类、代理类,并建立联表关系、配置逻辑删除属性:
用户表实体类(cn.duozai.sadmin.repository.UsersEntity):
/**
* 用户表 实体类。
*
* @author easy-query-plugin automatic generation
* @since 1.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
@Table(value = "users")
@EntityProxy
public class UsersEntity implements ProxyEntityAvailable<UsersEntity, UsersEntityProxy> {
// ...
/**
* 删除状态,0未删除
*
* @LogicDelete:配置逻辑删除
* @LogicDeleteStrategyEnum.DELETE_LONG_TIMESTAMP:逻辑删除字段为Long型,值为当前时间戳
* 执行删除时,将删除时间戳保存到deleted中
*/
@LogicDelete(strategy = LogicDeleteStrategyEnum.DELETE_LONG_TIMESTAMP)
private Long deleted;
/**
* 关联部门表实体
*/
@Navigate(value = RelationTypeEnum.ManyToOne, selfProperty = {UsersEntity.Fields.deptId}, targetProperty = {DeptEntity.Fields.id})
private DeptEntity dept;
/**
* 关联角色表实体
*/
@Navigate(value = RelationTypeEnum.ManyToOne, selfProperty = {UsersEntity.Fields.roleId}, targetProperty = {RoleEntity.Fields.id})
private RoleEntity role;
}其余实体类操作、修改方式一致。
Git
Git 是目前最流行的分布式版本控制系统,核心价值是解决多人协作开发和代码变更追溯的痛点,现在几乎所有互联网公司、开源项目都用 Git 管理代码。
主流的 Git 代码托管平台:GitHub、Gitee、GitCode、Gitea(私有化)、Gitlab(私有化)等。
项目开发过程,选择使用 GitHub 进行代码托管。
GitHub 文档:https://github.com/
在 GitHub 上创建一个远程仓库:
仓库创建完成后,根据 GitHub 提示在创建本地仓库并与远程仓库建立关联,将后端项目推送至 Github:
构建前端项目
Vue 3
在终端中执行指令,创建 Vue 3 项目:
npm init vue@latest使用 IntelliJ IDEA 打开创建的 Vue 3 前端项目,并安装依赖项:
ElementPlus
ElementPlus 是一款基于 Vue 3,面向设计师和开发者的组件库。
ElementPlus 文档:https://element-plus.org/zh-CN/
在终端中执行指令,安装 ElementPlus:
npm install element-plus --save编写项目入口文件(main.js),完整引入 ElementPlus:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
// 完整引入ElementPlus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
// 安装ElementPlus
app.use(ElementPlus)
app.mount('#app')Axios
Axios 是一个基于 Promise 的 HTTP 库,可以发送 GET、POST、PUT、DELETE 等请求。
Axios 文档:http://www.axios-js.com/
在终端中执行指令,安装 Axios:
npm install axios编写 Axios 配置文件(plugins/axios.js):
"use strict";
import axios from "axios";
// Axios配置
let config = {
// 统一请求前缀
baseURL: "http://localhost:8080"
};
const _axios = axios.create(config);
// Axios请求拦截器
_axios.interceptors.request.use(
function(config) {
// Do something before request is sent
return config;
},
function(error) {
// Do something with request error
return Promise.reject(error);
}
);
// Axios响应拦截器
_axios.interceptors.response.use(
function(response) {
// Do something with response data
return response;
},
function(error) {
// Do something with response error
return Promise.reject(error);
}
);
const Plugin = {
install(app, options) {
app.axios = _axios;
window.axios = _axios;
}
}
export default Plugin;编写项目入口文件(main.js),引入 Axios:
// 引入Axios
import axios from '@/plugins/axios.js'
// 安装Axios
app.use(axios)QS
QS 是一款数据序列化工具,可以将 JSON 对象转为 URL 编码的表单格式字符串(key1=value1&key2=value2),Axios 常搭配 QS 使用,将默认传递的 JSON 参数转换成表单格式字符串。
QS 文档:https://www.npmjs.com/package/qs
在终端中执行指令,安装 QS:
npm install qsGit
在 GitHub 上创建一个远程仓库,用于存放前端项目。
前端项目与后端项目的仓库是分开的,需要分别创建,创建、初始化、推送方式一致。
文章中可能会存在些许错别字内容描述不完整、表述不准确、排版布局异常等问题,文章中提及的软件、依赖、框架等程序可能随其版本更新迭代而产生变化,文章中的相关代码片段、例图、文本等内容仅供参考。
如若转载,请注明出处:https://www.duox.dev/post/119.html
The hint of this terpene fuse - granddaddy purple marijuana strain is exceedingly good and fundamental, not too concentrated but mollify clear-cut in the superlative way. It blends smoothly and adds a much control superiors flavor diagram without powerful the entirety else. Equanimous a slight amount makes a difference, which says a plight around the quality. The packaging was healthy, shipping was faithful, and the unimpaired experience felt reliable. Unquestionably solid product and everyone I’d providentially order again.
This legal mushrooms gummies felt like a huge balanced option. It wasn’t too ample and didn’t needy too further in the other direction either, which made it a all-round pick pro different parts of the day. The flower looked премиум, nicely trimmed, and tranquillity full of vim preferably of dry and crumbly. The flavor came in all respects disinfected with a degree perfumed and shameless note that мейд it enjoyable all the feeling through. Plainly a by-product that feels consistent and genially place together.
The atmosphere of this terpene fuse - https://terpenewarehouse.com/products/forbidden-fruit-terpenes is exceedingly good and ingenuous, not too heady but mollify unmistakable in the overcome way. It blends smoothly and adds a much bettor flavor profile without oppressive the entirety else. Equanimous a small amount makes a balance, which says a loads with reference to the quality. The packaging was healthy, shipping was rakish, and the unimpaired adventure felt reliable. Indeed solid spin-off and joined I’d happily order again.