作为一名有十年全栈开发经验的工程师,我经常被问到如何设计一个既实用又具备教学价值的毕业设计项目。今天要分享的这套工程师运维服务管理系统,正是结合了企业实际需求与教学实践的典型案例。这个系统采用SpringBoot+Vue+MySQL技术栈实现,完整覆盖了从需求分析到部署上线的全流程。
这个系统的核心价值在于:
我在设计这个系统时,特别考虑了教学演示和企业应用的双重需求。下面就从技术选型开始,详细解析这个项目的设计思路和实现细节。
在技术选型阶段,我对比了多种技术方案,最终确定这个技术栈主要基于以下考量:
开发效率方面:
学习成本方面:
性能考量:
系统采用经典的三层架构:
code复制表示层(View) → Vue前端组件
业务逻辑层(Controller) → SpringBoot REST API
数据访问层(Model) → MyBatis-Plus + MySQL
这种分层带来的好处是:
以工单创建为例的完整请求流程:
提示:在实际开发中,建议使用Swagger自动生成API文档,可以大幅减少前后端联调时的沟通成本。
根据运维系统的业务特点,我设计了以下主要数据表:
用户表(sys_user)
sql复制CREATE TABLE `sys_user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '登录账号',
`password` varchar(100) NOT NULL COMMENT '密码',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(20) DEFAULT NULL COMMENT '手机号',
`status` tinyint DEFAULT '1' COMMENT '状态 0:禁用 1:正常',
`dept_id` bigint DEFAULT NULL COMMENT '部门ID',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户';
工单表(ops_ticket)
sql复制CREATE TABLE `ops_ticket` (
`id` bigint NOT NULL AUTO_INCREMENT,
`ticket_no` varchar(20) NOT NULL COMMENT '工单编号',
`title` varchar(200) NOT NULL COMMENT '工单标题',
`content` text COMMENT '工单内容',
`priority` tinyint DEFAULT '2' COMMENT '优先级 1:低 2:中 3:高',
`status` tinyint DEFAULT '0' COMMENT '状态 0:待处理 1:处理中 2:已完成 3:已关闭',
`creator_id` bigint NOT NULL COMMENT '创建人ID',
`assignee_id` bigint DEFAULT NULL COMMENT '处理人ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `ticket_no` (`ticket_no`),
KEY `idx_status` (`status`),
KEY `idx_creator` (`creator_id`),
KEY `idx_assignee` (`assignee_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运维工单';
在项目开发过程中,我总结了以下数据库优化经验:
索引优化:
SQL优化:
java复制// 错误示例 - N+1查询问题
List<User> users = userMapper.selectList(null);
for(User user : users) {
Department dept = departmentMapper.selectById(user.getDeptId());
user.setDept(dept);
}
// 正确做法 - 使用连表查询
@Select("SELECT u.*, d.name as dept_name FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.id")
List<UserVO> selectUserWithDept();
连接池配置:
yaml复制# application.yml
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 30000
工单流转是系统的核心业务,我采用状态模式来实现:
java复制public interface TicketState {
void handle(TicketContext context);
}
@Component
public class PendingState implements TicketState {
@Override
public void handle(TicketContext context) {
Ticket ticket = context.getTicket();
// 待处理状态的业务逻辑
if("ASSIGN".equals(context.getAction())) {
ticket.setStatus(1); // 转为处理中
ticket.setAssigneeId(context.getOperatorId());
// 发送通知
notifyService.sendAssignNotice(ticket);
}
}
}
@Service
public class TicketService {
@Autowired
private Map<String, TicketState> stateMap;
public void processTicket(Long ticketId, String action) {
Ticket ticket = ticketMapper.selectById(ticketId);
TicketContext context = new TicketContext(ticket, action);
TicketState state = stateMap.get(ticket.getStatusName()+"State");
state.handle(context);
ticketMapper.updateById(ticket);
}
}
系统采用RBAC权限模型,关键实现如下:
数据库表设计:
Spring Security配置:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/ticket/create").hasAuthority("ticket:create")
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
前端权限控制:
javascript复制// 动态路由配置
router.beforeEach((to, from, next) => {
if (to.meta.roles && !store.getters.roles.some(role => to.meta.roles.includes(role))) {
next('/403')
} else {
next()
}
})
// 按钮级权限控制
Vue.directive('permission', {
inserted(el, binding) {
const { value } = binding
const roles = store.getters.roles
if (value && !roles.includes(value)) {
el.parentNode && el.parentNode.removeChild(el)
}
}
})
推荐两种部署方式:
传统JAR包部署:
bash复制# 打包
mvn clean package -DskipTests
# 运行
java -jar target/ops-manager.jar --spring.profiles.active=prod
# 使用nohup保持后台运行
nohup java -jar target/ops-manager.jar > app.log 2>&1 &
Docker容器化部署:
dockerfile复制# Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/ops-manager.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
bash复制# 构建镜像
docker build -t ops-manager .
# 运行容器
docker run -d -p 8080:8080 \
-e SPRING_DATASOURCE_URL=jdbc:mysql://mysql-server:3306/ops \
-e SPRING_DATASOURCE_USERNAME=root \
-e SPRING_DATASOURCE_PASSWORD=123456 \
--name ops-manager ops-manager
生产环境构建:
bash复制npm run build
Nginx配置示例:
nginx复制server {
listen 80;
server_name ops.example.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
如果想让这个项目在毕业答辩中脱颖而出,可以考虑以下扩展方向:
智能化扩展:
可视化大屏:
微服务改造:
移动端适配:
这个项目我已经在实际教学中使用了3个学期,迭代了5个版本,积累了大量实用经验。对于想深入学习SpringBoot全栈开发的同学,按照这个项目实践一遍,可以系统掌握企业级应用开发的完整流程。