graph TD
A[Vue 前端] -->|HTTP 请求| B(Spring Boot 后端)
B -->|JDBC/ORM| C[(数据库)]
C -->|返回数据| B
B -->|JSON 响应| A
A -->|状态管理| D[Vuex Store]
B -->|缓存| E[Redis]
B -->|消息队列| F[RabbitMQ/Kafka]
CREATE TABLE `sys_user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` VARCHAR(50) NOT NULL UNIQUE COMMENT '登录账号',
`password` VARCHAR(100) NOT NULL COMMENT '加密后的密码',
`nickname` VARCHAR(50) COMMENT '用户昵称',
`email` VARCHAR(100) COMMENT '电子邮箱',
`status` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态 0-禁用 1-正常',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
INDEX `idx_username` (`username`),
INDEX `idx_email` (`email`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
• 组合索引遵循最左前缀原则
• WHERE 条件中的高筛选率字段优先
• 避免在频繁更新的字段上建索引
• 使用覆盖索引优化查询性能
src/main/java
├── config // 配置类
├── controller // REST API 入口
├── service // 业务逻辑层
│ ├── impl // 接口实现
├── dao/repository // 数据访问层
├── entity/domain // 实体类
├── dto // 数据传输对象
├── vo // 视图对象
├── exception // 自定义异常
├── interceptor // 拦截器
├── filter // 过滤器
└── util // 工具类
@RestController
@RequestMapping("/api/users")
@Tag(name = "用户管理", description = "用户相关操作")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
@Operation(summary = "创建用户")
public ResponseEntity> createUser(@Valid @RequestBody UserCreateDTO dto) {
UserVO createdUser = userService.createUser(dto);
return ResponseEntity.created(URI.create("/users/"+createdUser.getId()))
.body(ResultVO.success(createdUser));
}
@GetMapping("/{id}")
@Operation(summary = "获取用户详情")
public ResponseEntity> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(ResultVO.success(userService.getUserById(id)));
}
}
src/
├── api/ // 接口定义
├── assets/ // 静态资源
├── components/ // 公共组件
├── router/ // 路由配置
├── store/ // Vuex 状态管理
├── utils/ // 工具函数
├── views/ // 页面组件
└── main.js // 入口文件
// api/user.js
import request from '@/utils/request'
export function login(data) {
return request({
url: '/auth/login',
method: 'post',
data
})
}
export function getUserInfo() {
return request({
url: '/api/users/me',
method: 'get'
})
}
// utils/request.js
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 10000
})
// 请求拦截
service.interceptors.request.use(
config => {
if (store.getters.token) {
config.headers['Authorization'] = 'Bearer ' + getToken()
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
Message.error(res.message || 'Error')
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res.data
}
},
error => {
Message.error(error.message)
return Promise.reject(error)
}
)
提交
POST /api/users HTTP/1.1
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
{
"username": "testuser",
"password": "P@ssw0rd!"
}
sequenceDiagram
participant C as Controller
participant S as Service
participant D as DAO
participant DB as Database
C->>S: 接收DTO并校验
S->>D: 调用数据访问方法
D->>DB: 执行SQL
DB-->>D: 返回结果集
D-->>S: 返回Entity
S->>S: 业务逻辑处理
S->>C: 返回VO对象
C-->>Client: 封装ResultVO响应
@Transactional(rollbackFor = Exception.class)
public UserVO createUser(UserCreateDTO dto) {
// 1. 数据校验
validateUserInfo(dto);
// 2. 转换对象
User user = convertToEntity(dto);
// 3. 保存到数据库
userRepository.save(user);
// 4. 发送领域事件
eventPublisher.publishEvent(new UserCreatedEvent(user));
return convertToVO(user);
}
.then(response => {
// 更新本地状态
store.commit('user/addUser', response.data)
// 刷新列表数据
fetchUserList()
// 提示成功
ElMessage.success('创建成功')
})
graph LR
A[登录请求] --> B[验证用户名密码]
B --> C[生成JWT]
C --> D[返回Token给客户端]
D --> E[后续请求携带Token]
E --> F[验证Token有效性]
F --> G[访问受保护资源]
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
@PostMapping("/upload")
public ResultVO uploadFile(@RequestParam("file") MultipartFile file) {
String filePath = fileStorageService.store(file);
return ResultVO.success(filePath);
}
点击上传
SQL 注入防护
XSS 防护
http.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
二级缓存配置(MyBatis)
接口响应优化
@Query("SELECT new com.example.dto.UserDTO(u.id, u.username) FROM User u")
List findAllUserDTOs();