Role
你是一位拥有10年经验的全栈开发专家,精通 Java Spring Boot 后端架构与 Vue 3 前端生态。你特别擅长使用 **MyBatis-Plus** 进行高效的数据持久层开发。
Project Goal
请为我生成一个基于 **Spring Boot + Vue 3 + MySQL + MyBatis-Plus** 的人员管理系统(Employee Management System)的核心代码框架。
Tech Stack Constraints
-
**后端**: Java 17 (或 1.8), Spring Boot 3.x, **MyBatis-Plus 3.5.x**, MySQL 8.0。
-
**前端**: Vue 3 (Composition API + `<script setup>`), Vite, Axios, Element Plus (UI 组件库)。
-
**工具库**: Lombok, Hutool (可选)。
Functional Requirements
请按照以下模块提供核心代码实现:
- **数据库设计 (MySQL)**
-
提供 `sys_user` (用户表) 和 `sys_employee` (员工信息表) 的 SQL 建表语句。
-
字段需包含:id (主键自增), username, password, name, email, phone, status, create_time, update_time, is_deleted (逻辑删除字段)。
- **后端实现 (Spring Boot + MyBatis-Plus)**
-
**配置**: 提供 `application.yml` 的关键配置(数据库连接、MP 配置如逻辑删除、分页插件)。
-
**实体层**: `User` 和 `Employee` 实体类,使用 MP 的注解(如 `@TableName`, `@TableId`, `@TableLogic`)。
-
**持久层**: 定义 `EmployeeMapper` 接口,继承 **`BaseMapper<Employee>`**。
-
**业务层**: `EmployeeService` 接口继承 **`IService<Employee>`**,实现类继承 **`ServiceImpl<Mapper, Entity>`**。请展示如何使用 **`LambdaQueryWrapper`** 进行条件查询。
-
**控制层**: `AuthController` (登录/注册) 和 `EmployeeController`。
-
**通用封装**: 提供统一的响应结果类 `Result<T>` (包含 code, msg, data) 和分页结果封装。
- **前端实现 (Vue 3)**
-
**API 封装**: 使用 Axios 封装请求拦截器(处理 Token)和 API 接口调用函数。
-
**视图层**:
-
**登录/注册页**: 包含表单验证逻辑。
-
**人员管理页**: 使用 Element Plus 的 Table 和 Pagination 组件,实现列表展示(分页)、新增/编辑弹窗、删除(调用后端逻辑删除或物理删除)。
Output Rules
-
代码需要包含关键注释,特别是 MP 特有的注解解释。
-
重点展示 MP 如何简化 CRUD 操作(例如不需要写 XML,直接使用 `baseMapper.selectList` 等)。
-
请按模块分块展示核心代码。
数据库设计(MySQL)
sql
-- 用户表(系统账号)
CREATE TABLE `sys_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` varchar(64) NOT NULL COMMENT '登录账号',
`password` varchar(128) NOT NULL COMMENT '加密密码',
`name` varchar(32) DEFAULT NULL COMMENT '真实姓名',
`email` varchar(128) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`status` tinyint DEFAULT '1' COMMENT '状态(0禁用 1正常)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint DEFAULT '0' COMMENT '逻辑删除(0未删 1已删)',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统用户表';
-- 员工信息表(业务数据)
CREATE TABLE `sys_employee` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` bigint NOT NULL COMMENT '关联用户ID',
`name` varchar(32) NOT NULL COMMENT '员工姓名',
`gender` tinyint DEFAULT '0' COMMENT '性别(0未知 1男 2女)',
`email` varchar(128) DEFAULT NULL COMMENT '工作邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
`department` varchar(64) DEFAULT NULL COMMENT '所属部门',
`position` varchar(64) DEFAULT NULL COMMENT '职位',
`status` tinyint DEFAULT '1' COMMENT '在职状态(0离职 1在职)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint DEFAULT '0' COMMENT '逻辑删除(0未删 1已删)',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='员工信息表';
后端核心实现
配置文件(application.yml)
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emp_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: isDeleted # 全局逻辑删除字段
logic-not-delete-value: 0 # 未删除值
logic-delete-value: 1 # 已删除值
type-aliases-package: com.example.ems.model.entity
实体类(使用MyBatis-Plus注解)
java
// User.java
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String name;
private String email;
private String phone;
private Integer status;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer isDeleted;
}
// Employee.java
@Data
@TableName("sys_employee")
public class Employee {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private String name;
private Integer gender;
private String email;
private String phone;
private String department;
private String position;
private Integer status;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer isDeleted;
}
Mapper接口(继承BaseMapper)
java
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
// 所有基础CRUD方法已通过BaseMapper继承
}
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 自定义查询示例
@Select("SELECT * FROM sys_user WHERE username = #{username} AND is_deleted = 0")
User selectByUsername(@Param("username") String username);
}
Service层实现
java
// 服务接口
public interface EmployeeService extends IService<Employee> {
Page<Employee> listByCondition(EmployeeQueryDTO dto);
}
// 服务实现类
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
@Override
public Page<Employee> listByCondition(EmployeeQueryDTO dto) {
// 使用LambdaQueryWrapper构建条件查询
return page(new Page<>(dto.getPageNum(), dto.getPageSize()),
new LambdaQueryWrapper<Employee>()
.like(StringUtils.isNotBlank(dto.getName()), Employee::getName, dto.getName())
.eq(dto.getDepartment() != null, Employee::getDepartment, dto.getDepartment())
.eq(dto.getStatus() != null, Employee::getStatus, dto.getStatus())
.orderByDesc(Employee::getCreateTime)
);
}
}
Controller示例
java
@RestController
@RequestMapping("/api/employee")
@RequiredArgsConstructor
public class EmployeeController {
private final EmployeeService employeeService;
@GetMapping("/page")
public Result<Page<Employee>> pageList(EmployeeQueryDTO dto) {
return Result.success(employeeService.listByCondition(dto));
}
@PostMapping
public Result<Void> addEmployee(@RequestBody @Valid EmployeeDTO dto) {
Employee employee = new Employee();
BeanUtil.copyProperties(dto, employee);
employeeService.save(employee);
return Result.success();
}
@DeleteMapping("/{id}")
public Result<Void> deleteEmployee(@PathVariable Long id) {
// MP会自动处理逻辑删除
employeeService.removeById(id);
return Result.success();
}
}
统一响应封装
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private Integer code;
private String msg;
private T data;
public static <T> Result<T> success(T data) {
return new Result<>(200, "success", data);
}
public static Result<Void> success() {
return success(null);
}
}
前端核心实现(Vue3)
Axios封装
javascript
// api/request.js
import axios from 'axios'
import { ElMessage } from 'element-plus'
const service = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000
})
// 请求拦截器(处理Token)
service.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
})
// 响应拦截器(统一处理错误)
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
ElMessage.error(res.msg || 'Error')
return Promise.reject(new Error(res.msg || 'Error'))
}
return res.data
},
error => {
ElMessage.error(error.message || 'Request Error')
return Promise.reject(error)
}
)
export default service
API接口封装
javascript
// api/employee.js
import request from './request'
export function getEmployeeList(params) {
return request({
url: '/api/employee/page',
method: 'get',
params
})
}
export function addEmployee(data) {
return request({
url: '/api/employee',
method: 'post',
data
})
}
export function deleteEmployee(id) {
return request({
url: `/api/employee/${id}`,
method: 'delete'
})
}
员工列表组件
vue
<script setup>
import { ref, onMounted } from 'vue'
import { getEmployeeList, deleteEmployee } from '@/api/employee'
const tableData = ref([])
const loading = ref(false)
const pagination = ref({
pageNum: 1,
pageSize: 10,
total: 0
})
const fetchData = async () => {
loading.value = true
try {
const res = await getEmployeeList({
...pagination.value
})
tableData.value = res.records
pagination.value.total = res.total
} finally {
loading.value = false
}
}
const handleDelete = async (id) => {
await deleteEmployee(id)
ElMessage.success('删除成功')
fetchData()
}
onMounted(() => {
fetchData()
})
</script>
<template>
<el-table :data="tableData" v-loading="loading">
<el-table-column prop="name" label="姓名" />
<el-table-column prop="department" label="部门" />
<el-table-column prop="position" label="职位" />
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
@current-change="fetchData"
layout="total, prev, pager, next"
/>
</template>
MyBatis-Plus特性应用说明
自动填充功能
通过@TableField(fill = FieldFill.INSERT)等注解配合元对象处理器实现:
java
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
条件构造器最佳实践
java
// 复杂查询示例
LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
wrapper.select(Employee::getId, Employee::getName, Employee::getDepartment)
.likeRight(Employee::getName, "张")
.between(Employee::getCreateTime, startDate, endDate)
.orderByAsc(Employee::getDepartment)
.orderByDesc(Employee::getCreateTime);
List<Employee> list = employeeMapper.selectList(wrapper);
逻辑删除实现原理
配置全局逻辑删除字段后,MP会自动在所有查询/删除操作中添加条件:
sql
SELECT * FROM sys_employee WHERE is_deleted = 0
DELETE FROM sys_employee WHERE id = ? AND is_deleted = 0
实际执行的删除操作变为更新:
sql
UPDATE sys_employee SET is_deleted = 1 WHERE id = ?