
最近不少计算机专业的同学都在为毕业设计发愁选题、需求、技术栈、实现……每一步都让人头大。特别是“校企实习管理系统”这类经典选题看似简单但要做到功能完整、逻辑清晰、代码规范还得有实际应用价值确实需要一套清晰的指导。本文将为你完整拆解一个“校企实习管理系统”从零到一的开发全过程。你不用再自己绞尽脑汁构思需求我们将基于一个成熟、通用的业务模型带你一步步完成环境搭建、数据库设计、核心功能实现、前后端联调直到最终部署。文章包含完整的代码示例、清晰的配置说明和避坑指南无论是 Java Spring Boot 新手还是希望系统学习项目开发流程的同学都能直接上手快速搭建出一个可用于毕设或课程设计的完整项目。1. 项目背景与核心需求分析在开始编码之前我们必须明确系统要解决什么问题以及为谁服务。一个典型的校企实习管理系统核心目标是搭建一个连接学校、企业和学生的数字化平台实现实习流程的线上化、规范化管理。1.1 系统核心角色与痛点系统主要涉及三类用户每类用户都有其核心诉求学生痛点在于寻找实习机会困难、投递简历流程繁琐、实习过程缺乏有效跟踪与指导、实习报告提交与考核不透明。企业痛点在于招聘实习生渠道有限、筛选简历工作量大、难以系统化管理实习生入职、在岗及考核全流程。学校/导师痛点在于难以掌握学生实习的真实情况地点、岗位、安全等、实习过程管理分散、成绩评定缺乏数据支撑、与企业沟通效率低。1.2 系统核心功能模块基于以上痛点我们可以将系统拆解为以下几个核心功能模块权限与用户管理实现学生、企业、导师、管理员等多角色登录、注册及权限控制。实习岗位管理企业发布、修改、下架实习岗位学生浏览、搜索、收藏、投递岗位。简历与投递管理学生创建并维护个人简历查看投递状态已投递、已查看、通过初筛、面试邀请、已录用、已拒绝。实习过程管理学生提交周报/月报企业或导师进行批阅与评分关键事件记录。考核与评价管理实习结束后企业对学生进行评价学生对企业/岗位进行反馈导师综合评定最终实习成绩。公告与消息系统系统通知、实习相关公告、站内信等。1.3 技术栈选型说明为了高效、稳定地实现上述功能我们选择一套当前企业级开发中主流、生态成熟的技术栈后端Spring Boot 2.7.x。它极大地简化了Spring应用的初始搭建和开发过程内嵌Tomcat约定优于配置。数据库MySQL 8.0。关系型数据库适合存储结构化的用户、岗位、投递记录等数据。持久层框架MyBatis-Plus。在MyBatis基础上增强提供了通用的CRUD操作减少大量SQL编写。权限控制Spring Security JWT (JSON Web Token)。用于接口级别的认证与授权保障系统安全。前端Vue 3 Element Plus。渐进式JavaScript框架配合成熟的UI组件库快速构建美观的管理界面。项目管理Maven。用于项目构建和依赖管理。2. 开发环境准备与项目初始化“工欲善其事必先利其器”。在开始写代码前请确保你的本地开发环境已就绪。2.1 基础环境安装请确保你的计算机上已安装以下软件并配置好环境变量JDK版本 8 或 11推荐11。安装后通过java -version验证。Maven版本 3.6。安装后通过mvn -v验证。MySQL版本 5.7推荐8.0。安装并启动服务记住root密码。Node.js版本 16推荐18 LTS。安装后通过node -v和npm -v验证。IDEIntelliJ IDEA后端开发 和 Visual Studio Code前端开发。2.2 创建Spring Boot后端项目我们使用 Spring Initializr 快速初始化项目。访问 start.spring.io 。选择以下配置Project: Maven ProjectLanguage: JavaSpring Boot: 2.7.x (选择一个稳定版本如 2.7.18)Project Metadata:Group:com.exampleArtifact:internship-systemPackaging: JarJava: 11Dependencies: 添加Spring Web,Spring Security,MyBatis Framework,MySQL Driver,Lombok。点击“Generate”下载项目压缩包解压后用IDEA打开。2.3 初始化数据库打开MySQL客户端如命令行或Navicat执行以下SQL创建数据库和用户表后续其他表会在项目启动后通过MyBatis-Plus自动生成。-- 创建数据库 CREATE DATABASE IF NOT EXISTS internship_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE internship_db; -- 用户表核心基础表 CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL COMMENT 用户名登录账号, password varchar(255) NOT NULL COMMENT 加密后的密码, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名, email varchar(100) DEFAULT NULL COMMENT 邮箱, phone varchar(20) DEFAULT NULL COMMENT 手机号, avatar varchar(500) DEFAULT NULL COMMENT 头像URL, role varchar(20) NOT NULL COMMENT 角色STUDENT, ENTERPRISE, TEACHER, ADMIN, 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 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT系统用户表;2.4 前端Vue项目初始化打开终端命令行执行以下命令创建Vue项目。# 使用Vue官方脚手架创建项目 npm create vuelatest internship-system-frontend # 创建过程中根据提示选择以下特性按空格选择/取消 # - TypeScript: 否 (为简化本项目先用JavaScript) # - JSX: 否 # - Vue Router: 是 # - Pinia: 是 (状态管理) # - ESLint: 是 # - Prettier: 是 # 进入项目目录并安装Element Plus和axios cd internship-system-frontend npm install npm install element-plus axios npm install --save-dev sass3. 后端核心模块设计与实现后端是整个系统的大脑负责业务逻辑、数据持久化和API提供。我们将采用分层架构Controller - Service - Mapper (DAO) - Model。3.1 项目结构规划在IDEA中你的后端项目src/main/java/com/example/internshipsystem目录结构应如下src/main/java/com/example/internshipsystem/ ├── InternshipSystemApplication.java // 启动类 ├── config/ // 配置类安全、跨域等 ├── controller/ // 控制层接收请求返回响应 ├── entity/ // 实体类与数据库表对应 ├── dto/ // 数据传输对象用于前后端交互 ├── vo/ // 视图对象用于返回给前端的数据封装 ├── mapper/ // 数据访问层接口MyBatis Mapper ├── service/ // 业务逻辑层接口 │ └── impl/ // 业务逻辑层实现 ├── utils/ // 工具类JWT、加密等 └── common/ // 通用类常量、异常、结果封装3.2 集成MyBatis-Plus与代码生成首先在pom.xml中添加MyBatis-Plus依赖替换掉之前Initializr生成的MyBatis依赖。!-- pom.xml -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- 代码生成器依赖可选用于快速生成entity, mapper, service -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-generator/artifactId version3.5.3.1/version scopetest/scope /dependency然后配置application.yml或application.properties连接数据库。这里以yml格式为例# src/main/resources/application.yml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/internship_db?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai username: root password: your_password # 替换为你的MySQL密码 # MyBatis-Plus 配置 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL开发时开启 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名如果表中有此字段 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值3.3 实现JWT认证与Spring Security配置认证与授权是管理系统的基石。我们将使用JWT实现无状态认证。创建JWT工具类用于生成和解析Token。// src/main/java/com/example/internshipsystem/utils/JwtUtil.java Component public class JwtUtil { private static final String SECRET_KEY your-secret-key-change-in-production; // 生产环境务必更改并妥善保管 private static final long EXPIRATION_TIME 86400000; // 24小时单位毫秒 public String generateToken(String username, String role) { MapString, Object claims new HashMap(); claims.put(role, role); return Jwts.builder() .setClaims(claims) .setSubject(username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } // ... 其他验证方法 }配置Spring Security自定义登录逻辑和权限过滤。// src/main/java/com/example/internshipsystem/config/SecurityConfig.java Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable() // 禁用CSRF因为使用JWT无状态 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话 .and() .authorizeRequests() .antMatchers(/api/auth/login, /api/auth/register).permitAll() // 登录注册公开 .antMatchers(/api/student/**).hasRole(STUDENT) // 学生接口需要学生角色 .antMatchers(/api/enterprise/**).hasRole(ENTERPRISE) // 企业接口需要企业角色 .antMatchers(/api/admin/**).hasRole(ADMIN) // 管理员接口 .anyRequest().authenticated() // 其他所有请求都需要认证 .and() .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // 添加JWT过滤器 return http.build(); } // 注册JWT认证过滤器Bean Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } }3.4 实现用户登录与注册接口创建认证相关的Controller、Service。// src/main/java/com/example/internshipsystem/controller/AuthController.java RestController RequestMapping(/api/auth) public class AuthController { Autowired private AuthService authService; PostMapping(/login) public ResultVOLoginVO login(RequestBody Valid LoginDTO loginDTO) { LoginVO loginVO authService.login(loginDTO); return ResultVO.success(loginVO); } PostMapping(/register) public ResultVOString register(RequestBody Valid RegisterDTO registerDTO) { authService.register(registerDTO); return ResultVO.success(注册成功); } }// src/main/java/com/example/internshipsystem/service/impl/AuthServiceImpl.java Service public class AuthServiceImpl implements AuthService { Autowired private SysUserMapper userMapper; Autowired private PasswordEncoder passwordEncoder; Autowired private JwtUtil jwtUtil; Override public LoginVO login(LoginDTO loginDTO) { SysUser user userMapper.selectOne(new LambdaQueryWrapperSysUser() .eq(SysUser::getUsername, loginDTO.getUsername())); if (user null || !passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) { throw new BusinessException(用户名或密码错误); } if (user.getStatus() 0) { throw new BusinessException(账号已被禁用); } // 生成JWT Token String token jwtUtil.generateToken(user.getUsername(), user.getRole()); LoginVO loginVO new LoginVO(); loginVO.setToken(token); loginVO.setUserInfo(convertToUserInfoVO(user)); return loginVO; } Override public void register(RegisterDTO registerDTO) { // 检查用户名是否已存在 Long count userMapper.selectCount(new LambdaQueryWrapperSysUser() .eq(SysUser::getUsername, registerDTO.getUsername())); if (count 0) { throw new BusinessException(用户名已存在); } SysUser user new SysUser(); BeanUtils.copyProperties(registerDTO, user); // 加密密码 user.setPassword(passwordEncoder.encode(registerDTO.getPassword())); user.setRole(registerDTO.getRole()); // STUDENT, ENTERPRISE, TEACHER user.setStatus(1); userMapper.insert(user); } }4. 核心业务功能实现以实习岗位管理为例我们以“实习岗位管理”这个核心业务模块为例展示从数据库表设计到前后端联调的完整流程。4.1 数据库表设计-- 实习岗位表 CREATE TABLE internship_position ( id bigint NOT NULL AUTO_INCREMENT, enterprise_id bigint NOT NULL COMMENT 发布企业ID, title varchar(200) NOT NULL COMMENT 岗位标题, description text COMMENT 岗位描述, requirement text COMMENT 任职要求, location varchar(100) DEFAULT NULL COMMENT 工作地点, category varchar(50) DEFAULT NULL COMMENT 岗位类别如技术、市场等, recruit_num int DEFAULT NULL COMMENT 招聘人数, apply_deadline date DEFAULT NULL COMMENT 申请截止日期, status tinyint DEFAULT 1 COMMENT 状态0-已下架1-招聘中2-已结束, view_count int DEFAULT 0 COMMENT 浏览量, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_enterprise (enterprise_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT实习岗位表; -- 岗位投递记录表 CREATE TABLE position_apply ( id bigint NOT NULL AUTO_INCREMENT, position_id bigint NOT NULL, student_id bigint NOT NULL, resume_id bigint DEFAULT NULL COMMENT 关联的简历ID, status varchar(20) DEFAULT PENDING COMMENT 状态PENDING-待处理, VIEWED-已查看, INTERVIEW-面试, OFFER-录用, REJECT-拒绝, apply_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_position_student (position_id,student_id), -- 防止重复投递 KEY idx_student (student_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT岗位投递记录表;4.2 后端实体与Mapper使用MyBatis-Plus我们只需创建Entity和Mapper接口。// src/main/java/com/example/internshipsystem/entity/InternshipPosition.java Data TableName(internship_position) public class InternshipPosition { TableId(type IdType.AUTO) private Long id; private Long enterpriseId; private String title; private String description; private String requirement; private String location; private String category; private Integer recruitNum; private Date applyDeadline; private Integer status; private Integer viewCount; private Date createTime; private Date updateTime; }// src/main/java/com/example/internshipsystem/mapper/InternshipPositionMapper.java Mapper public interface InternshipPositionMapper extends BaseMapperInternshipPosition { // 复杂的多表查询可以在这里定义XML或注解SQL Select(SELECT p.*, e.company_name FROM internship_position p LEFT JOIN sys_user e ON p.enterprise_id e.id WHERE p.status 1 ORDER BY p.create_time DESC) ListMapString, Object selectPositionWithEnterprise(); }4.3 业务层与服务实现创建Service层处理业务逻辑如状态校验、数据组装。// src/main/java/com/example/internshipsystem/service/InternshipPositionService.java public interface InternshipPositionService extends IServiceInternshipPosition { PageVOPositionVO getPositionPage(PageDTO pageDTO, PositionQueryDTO queryDTO); PositionDetailVO getPositionDetail(Long id); void publishPosition(PositionPublishDTO publishDTO, Long enterpriseId); void updatePosition(PositionUpdateDTO updateDTO, Long enterpriseId); void applyPosition(Long positionId, Long studentId); }在实现类中需要注入Mapper并处理具体的业务规则比如企业只能修改自己发布的岗位。4.4 控制层API设计设计清晰的RESTful API。// src/main/java/com/example/internshipsystem/controller/PositionController.java RestController RequestMapping(/api/position) public class PositionController { Autowired private InternshipPositionService positionService; // 分页查询岗位列表学生/所有人 GetMapping(/page) public ResultVOPageVOPositionVO getPositionPage(PageDTO pageDTO, PositionQueryDTO queryDTO) { PageVOPositionVO page positionService.getPositionPage(pageDTO, queryDTO); return ResultVO.success(page); } // 获取岗位详情 GetMapping(/{id}) public ResultVOPositionDetailVO getPositionDetail(PathVariable Long id) { PositionDetailVO detail positionService.getPositionDetail(id); return ResultVO.success(detail); } // 企业发布岗位 (需要ENTERPRISE角色) PostMapping(/publish) PreAuthorize(hasRole(ENTERPRISE)) public ResultVOString publishPosition(RequestBody Valid PositionPublishDTO publishDTO, RequestAttribute(userId) Long enterpriseId) { positionService.publishPosition(publishDTO, enterpriseId); return ResultVO.success(发布成功); } // 学生投递岗位 (需要STUDENT角色) PostMapping(/{positionId}/apply) PreAuthorize(hasRole(STUDENT)) public ResultVOString applyPosition(PathVariable Long positionId, RequestAttribute(userId) Long studentId) { positionService.applyPosition(positionId, studentId); return ResultVO.success(投递成功); } }5. 前端页面开发与联调前端使用Vue 3 Element Plus Axios构建一个单页面应用(SPA)。5.1 配置Axios与路由守卫首先在src/utils/request.js中创建配置好的axios实例统一处理请求拦截添加JWT Token和响应拦截处理错误。// src/utils/request.js import axios from axios; import { ElMessage } from element-plus; import router from /router; import { useUserStore } from /stores/user; const service axios.create({ baseURL: http://localhost:8080/api, // 后端API地址 timeout: 10000, }); // 请求拦截器 service.interceptors.request.use( (config) { const userStore useUserStore(); if (userStore.token) { config.headers[Authorization] Bearer ${userStore.token}; } return config; }, (error) { return Promise.reject(error); } ); // 响应拦截器 service.interceptors.response.use( (response) { const res response.data; if (res.code 200) { return res.data; } else { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message || Error)); } }, (error) { if (error.response?.status 401) { ElMessage.error(登录已过期请重新登录); router.push(/login); } else { ElMessage.error(error.message || 网络错误); } return Promise.reject(error); } ); export default service;在路由守卫中检查用户权限。// src/router/index.js router.beforeEach((to, from, next) { const userStore useUserStore(); if (to.meta.requiresAuth !userStore.token) { next(/login); } else if (to.meta.roles !to.meta.roles.includes(userStore.role)) { ElMessage.warning(权限不足); next(from.path); // 无权限则留在原地 } else { next(); } });5.2 实习岗位列表页开发使用Element Plus的表格和分页组件。!-- src/views/position/PositionList.vue -- template div classposition-list el-card template #header div classcard-header span实习岗位/span el-button v-ifuserStore.role ENTERPRISE typeprimary clickhandlePublish发布新岗位/el-button /div /template !-- 搜索区域 -- el-form :modelqueryParams inline el-form-item label岗位名称 el-input v-modelqueryParams.title placeholder请输入 clearable / /el-form-item el-form-item label地点 el-input v-modelqueryParams.location placeholder请输入 clearable / /el-form-item el-form-item el-button typeprimary clickhandleSearch搜索/el-button el-button clickhandleReset重置/el-button /el-form-item /el-form !-- 表格 -- el-table :datatableData v-loadingloading el-table-column proptitle label岗位标题 width200 / el-table-column propcompanyName label发布企业 width150 / el-table-column proplocation label工作地点 width120 / el-table-column propcategory label类别 width100 / el-table-column proprecruitNum label招聘人数 width100 / el-table-column propapplyDeadline label截止日期 width120 template #default{ row } {{ formatDate(row.applyDeadline) }} /template /el-table-column el-table-column label操作 width180 template #default{ row } el-button link typeprimary clickhandleViewDetail(row.id)查看详情/el-button el-button v-ifuserStore.role STUDENT link typesuccess clickhandleApply(row.id)立即投递/el-button /template /el-table-column /el-table !-- 分页 -- div classpagination el-pagination v-model:current-pagepageParams.pageNum v-model:page-sizepageParams.pageSize :totaltotal layouttotal, sizes, prev, pager, next, jumper current-changehandlePageChange size-changehandleSizeChange / /div /el-card /div /template script setup import { ref, onMounted } from vue; import { useRouter } from vue-router; import { ElMessage } from element-plus; import { getPositionPage, applyPosition } from /api/position; import { useUserStore } from /stores/user; import { formatDate } from /utils/date; const router useRouter(); const userStore useUserStore(); const loading ref(false); const tableData ref([]); const total ref(0); const pageParams ref({ pageNum: 1, pageSize: 10 }); const queryParams ref({ title: , location: }); const fetchData async () { loading.value true; try { const res await getPositionPage({ ...pageParams.value, ...queryParams.value }); tableData.value res.list; total.value res.total; } catch (error) { console.error(error); } finally { loading.value false; } }; const handleSearch () { pageParams.value.pageNum 1; fetchData(); }; const handleReset () { queryParams.value { title: , location: }; handleSearch(); }; const handlePageChange (val) { pageParams.value.pageNum val; fetchData(); }; const handleSizeChange (val) { pageParams.value.pageSize val; pageParams.value.pageNum 1; fetchData(); }; const handleViewDetail (id) { router.push(/position/detail/${id}); }; const handleApply async (positionId) { try { await applyPosition(positionId); ElMessage.success(投递成功); } catch (error) { ElMessage.error(error.message || 投递失败); } }; const handlePublish () { router.push(/position/publish); }; onMounted(() { fetchData(); }); /script5.3 前后端联调与测试启动后端在IDEA中运行InternshipSystemApplication的main方法。启动前端在终端进入前端项目目录运行npm run dev。测试接口使用浏览器访问http://localhost:5173(Vue默认端口)或使用Postman等工具直接测试后端APIhttp://localhost:8080/api/...。关键检查点跨域问题确保后端已配置CORS允许前端域名访问。Token传递检查浏览器开发者工具Network标签请求头中是否包含Authorization: Bearer token。权限控制尝试用学生账号访问企业发布接口应返回403无权限错误。数据一致性前端表格显示的数据是否与数据库查询结果一致。6. 常见问题与排查思路在开发过程中你可能会遇到以下典型问题。问题现象可能原因排查步骤与解决方案前端请求后端接口报4041. 后端服务未启动。2. 请求URL路径错误。3. 后端Controller未添加RestController或RequestMapping。1. 检查IDEA控制台确保Spring Boot启动成功。2. 核对前端request.js中的baseURL和后端接口路径。3. 检查后端Controller类和方法上的注解。前端请求报403 Forbidden或401 Unauthorized1. 接口需要认证但未携带Token。2. Token已过期。3. 用户角色权限不足。1. 检查是否已登录Token是否被正确设置在请求头中。2. 查看后端JWT工具类中设置的过期时间前端可在收到401时跳转登录页。3. 检查PreAuthorize注解或Security配置中的角色要求。数据库连接失败1. MySQL服务未启动。2.application.yml中数据库配置错误密码、端口、数据库名。3. 驱动版本不匹配。1. 使用mysql -u root -p测试MySQL连接。2. 仔细核对yml配置注意缩进和冒号后的空格。3. 确认pom.xml中MySQL驱动版本与安装的MySQL版本兼容。MyBatis-Plus 插入数据失败主键冲突实体类主键策略TableId配置错误或数据库表主键未设置自增。1. 实体类主键字段添加TableId(type IdType.AUTO)。2. 检查数据库表DDL主键字段是否设置了AUTO_INCREMENT。前端页面样式错乱或Element Plus组件不显示1. Element Plus未正确引入。2. Vue版本与Element Plus版本不兼容。1. 检查main.js中是否按官方文档正确引入了Element Plus及其样式。2. 查看package.json确保安装的element-plus版本支持你的Vue 3版本。修改代码后前端热更新不生效浏览器缓存或Vite开发服务器问题。1. 尝试硬刷新浏览器CtrlF5。2. 重启前端开发服务器npm run dev。7. 项目部署与上线建议完成开发后若想将项目部署到服务器供他人访问或用于毕设演示可以参考以下步骤。7.1 后端项目打包与部署打包在项目根目录下执行mvn clean package -DskipTests会在target目录下生成internship-system-0.0.1-SNAPSHOT.jar。环境配置将application.yml中的数据库连接等信息改为生产环境配置如服务器IP、正式数据库账号密码。切勿将配置文件中的密码等敏感信息提交到代码仓库建议使用环境变量或配置中心。上传与运行将jar包上传至Linux服务器如使用CentOS或Ubuntu。# 在服务器上运行后台运行并输出日志 nohup java -jar internship-system-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod app.log 21 # 查看日志 tail -f app.log7.2 前端项目构建与部署构建在前端项目目录下执行npm run build会在dist目录生成静态文件。部署将dist文件夹内的所有文件上传到Web服务器如Nginx、Apache的网站根目录。配置Nginx如果使用Nginx需要配置代理将API请求转发到后端服务。# /etc/nginx/conf.d/internship.conf server { listen 80; server_name your-domain.com; # 你的域名或IP # 前端静态资源 location / { root /path/to/your/dist; index index.html; try_files $uri $uri/ /index.html; # 支持Vue Router的history模式 } # 后端API代理 location /api/ { proxy_pass http://localhost:8080; # 后端服务地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }重启Nginxsudo systemctl restart nginx7.3 生产环境注意事项安全密码加密务必使用强哈希算法如BCrypt存储用户密码切勿明文存储。SQL注入使用MyBatis-Plus等ORM框架或严格使用参数化查询避免拼接SQL字符串。XSS与CSRF前端对用户输入进行转义后端可根据需要开启CSRF保护如果不用JWT。JWT Secret生产环境的JWT密钥必须足够复杂且妥善保管定期更换。性能数据库索引为高频查询条件如status,enterprise_id和关联字段建立索引。接口优化对于列表查询务必做好分页避免一次性拉取大量数据。静态资源使用CDN加速前端JS、CSS、图片等资源加载。可维护性日志配置完整的日志框架如Logback记录INFO、WARN、ERROR级别日志便于排查问题。监控考虑集成简单的健康检查接口 (/actuator/health)或使用Prometheus、SkyWalking等监控应用状态。文档编写清晰的API接口文档可使用Swagger/SpringDoc生成和项目部署文档。至此一个具备核心功能的“校企实习管理系统”从需求分析到部署上线的全流程已经走通。你可以在此基础上继续完善其他模块如周报管理、评价系统、消息通知增加更复杂的业务逻辑如岗位推荐算法或者优化UI/UX。这个项目不仅能够满足你的毕业设计需求其采用的分层架构、主流技术栈和开发流程也是你未来求职时一份非常不错的实战作品。