AI 摘要

本文首先设计 dept、role、perms、users、actionlog 五张核心业务表并给出完整 MySQL 脚本;随后用 Solon 构建轻量后端,集成 HikariCP、Redisx、SaToken、HuTool、ip2region 等组件,完成 ORM、权限、缓存、IP 定位、密码加盐、统一响应等基础设施;前端以 Vue 3 脚手架新建工程,引入 ElementPlus、Axios、QS,配置跨域与拦截器;最后将前后端分别推送到 GitHub,实现可协作的版本管理,为后续业务开发奠定标准化框架。

前置工作

开发环境

项目开发使用的开发环境及相关版本为 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):

字段名类型描述
idint部门 ID
namevarchar部门名称
deletedbigint删除状态,0 未删除
该字段用于逻辑删除

角色表(role):

字段名类型描述
idint角色 ID
namevarchar角色名称
permstext授权的权限菜单列表
remarksvarchar角色备注
deletedbigint删除状态,0 未删除
该字段用于逻辑删除

权限表(perms):

字段名类型描述
idint权限 ID
parent_idint父级权限 ID
namevarchar权限名称
identifiervarchar权限标识
pathint权限路由路径
componentvarchar权限路由组件
typeint权限类型,0 目录/1 菜单/2 操作
sort_idint排序 ID
statusint权限状态,1 显示/0 隐藏
deletedbigint删除状态,0 未删除
该字段用于逻辑删除

用户表(users):

字段名类型描述
idint用户 ID
dept_idint关联部门 ID
role_idint关联角色 ID
usernamevarchar账户账号
passwordvarchar账户密码
saltsvarchar账户盐值
realnamevarchar真实姓名
remarksvarchar用户备注
statusint账户状态,1 正常/0 禁用
deletedbigint删除状态,0 未删除
该字段用于逻辑删除

操作日志表(actionlog):

字段名类型描述
idint日志 ID
user_idint关联用户 ID
ipvarchar操作 IP 信息
addressvarchar操作 IP 地址
timestampbigint操作时间戳
titlevarchar日志标题
request_urlvarchar请求地址
request_methodvarchar请求方式
request_paramstext请求参数
response_resulttext响应结果

附数据库脚本:

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 '真实姓名',
  `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-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>

修改 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|福建省|厦门市|电信
            // 只需要提取省市县运营商,格式化字符串提取
            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));
    }

}

测试结果:

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 qs

Git

在 GitHub 上创建一个远程仓库,用于存放前端项目。

前端项目与后端项目的仓库是分开的,需要分别创建,创建、初始化、推送方式一致。