资讯动态

宠物猫认养系统毕设实战:SpringBoot+Vue前后端分离开发全流程

发布时间:2026/10/6 13:17:05 来源:尧图企业网站定制
做宠物猫认养系统这个毕设前后大概写了两个月中间推倒重来了一次。说实话选这个题目的时候很多同学的第一反应是“这不就是个增删改查吗”真正做完之后我才意识到它把Java Web毕设里该涉及的东西几乎全串起来了——SpringBoot做后端接口、Vue做前端页面、MySQL的SQL脚本、独立的接口文档再到前后端联调和部署每一步都是毕业设计答辩时老师会盯着的点。这个系统要解决的业务问题其实很具体管理员维护猫咪档案品种、年龄、健康状况、照片普通用户浏览猫咪列表、查看详情、提交认养申请管理员对申请进行审核审核通过后猫咪状态变成“已认养”同时还有公告模块负责发布领养须知和平台通知。看起来是几个独立的增删改查页面但认养申请这条状态链路一旦设计不清后面全是坑。接下来的内容我不会只贴一堆代码而是按我当时做项目的顺序把每个环节怎么思考、为什么这么选、踩了哪些坑都讲清楚。如果你正在准备Java Web方向的毕设或者想找一个结构完整、能跑通全流程的项目参考这篇内容可以帮你把整条链路理顺。1. 选这个题目之前我先把业务闭环想清楚了1.1 表面是增删改查实际是“状态驱动”的业务流最开始拿到这个题目我的第一反应是做一个猫咪信息列表能上传照片能删改看起来就差不多了。后来跟指导老师聊了一次他说了一句让我印象很深的话“认养系统里最关键的不是猫咪表格而是认养申请这条状态链路。”这句话基本决定了后面的设计方向。认养系统的核心业务流是这样的管理员或送养人发布猫咪信息 → 猫咪进入“待认养”状态 → 用户浏览猫咪列表并查看详情 → 用户提交认养申请 → 管理员审核申请 → 审核通过的猫咪标记为“已认养”。任何一个环节断了系统都不完整答辩的时候也很难讲出逻辑性。所以我做的第一件事不是建工程而是把角色和动作列出来。系统里有两类角色管理员和普通用户。管理员做的事包括登录、猫咪信息的新增/修改/删除/上下架、认养申请的审核、公告发布普通用户做的事包括注册登录、浏览猫咪、查看详情、提交认养申请、查看自己的申请进度。一个功能清单列完后端接口有哪些、数据库表有哪些、前端页面有哪些全部跟着出来了。1.2 技术栈选择最终落在了“能答辩”和“能跑通”之间技术选型我纠结过一阵子。参考了不少学长留下的代码和网上的毕设教程最后定了这套组合环节我用的方案备选方案为什么这么选后端框架SpringBoot 2.7.xSpringBoot 3.x3.x要求JDK 17起步包名从javax改成jakarta网上的老教程很多直接不兼容JDK版本JDK 8JDK 11/17实验室环境、远程服务器兼容性最好报错最少ORMMyBatis-Plus原生MyBatis分页插件、LambdaQueryWrapper、自动填充时间戳能省一大半繁琐代码数据库MySQL 8.0MySQL 5.78.0是当前主流注意连接驱动和时区参数认证方案JWT 拦截器Spring Security Session毕设场景下JWT的代码量小、逻辑直观答辩时好解释前端框架Vue3 Vite Pinia Element PlusVue2 Vuex ElementUIVue3是当前趋势Vite启动快Element Plus组件生态成熟接口文档Apifox导出MarkdownSwagger/knife4j项目要求提交独立文档Apifox可以直接生成离线版本这里多说一句SpringBoot版本的事。网上很多教程直接给你贴SpringBoot 3.x的代码如果你照抄第一步就会遇到javax.servlet不存在这种报错。我用的SpringBoot 2.7.x搭配JDK 8在毕设场景下是最稳妥的组合几乎你搜到的任何资料都能直接用不会卡在环境问题上。前端我最终选了Vue3。有些同学担心自己学的是Vue2其实核心的组件、生命周期、路由思路是通用的Vue3反而让状态管理更简单Pinia比Vuex直观很多。选Element Plus是因为后台管理页面需要大量表格、表单、弹窗组件现成组件能省下大量样式调试时间。2. 数据库设计SQL脚本不是一个表一个表堆出来的2.1 先列业务实体再落表结构我设计表的时候不是直接打开Navicat建表而是先把业务实体过了一遍用户、猫咪、认养申请、公告、分类。这五张表基本覆盖了系统的全部核心功能接下来就是梳理它们之间的关系。一个用户可以有多个认养申请每只猫咪在同一时间只能被一个申请最终认养但可以收到多个申请。这构成了“用户一对多申请申请多对一猫咪”的关系。公告和分类相对独立都是单表。我当时写接口文档和SQL脚本的时候专门画了一张表关系说明放在文档开头这份材料在最后的毕设答辩里非常加分因为老师第一眼就能看出你对整个数据结构是心里有数的。2.2 核心表DDL与关键字段设计用户表是所有系统的地基。我设计的时候把登录密码加密存储角色字段用tinyint为了以后扩展更多角色CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 用户ID, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码BCrypt加密, nickname varchar(50) DEFAULT NULL COMMENT 昵称, phone varchar(20) DEFAULT NULL COMMENT 手机号, avatar varchar(255) DEFAULT NULL COMMENT 头像URL, role tinyint NOT NULL DEFAULT 0 COMMENT 角色0普通用户 1管理员, status tinyint NOT NULL DEFAULT 1 COMMENT 状态1正常 0禁用, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;猫咪表是核心业务表里面很多字段都是给列表页和详情页直接用的。我设计了品种、年龄、性别、毛色、健康状态、疫苗情况、描述、图片地址以及最重要的status状态字段它要能表达猫咪在认养流程中的位置CREATE TABLE cat ( id bigint NOT NULL AUTO_INCREMENT COMMENT 猫咪ID, name varchar(50) NOT NULL COMMENT 猫咪昵称, category_id bigint DEFAULT NULL COMMENT 分类ID, breed varchar(50) DEFAULT NULL COMMENT 品种, age int DEFAULT NULL COMMENT 年龄月, gender tinyint DEFAULT NULL COMMENT 性别0公 1母, color varchar(50) DEFAULT NULL COMMENT 毛色, health_status varchar(100) DEFAULT NULL COMMENT 健康状况描述, vaccinated tinyint DEFAULT 0 COMMENT 是否已打疫苗1是 0否, description text COMMENT 详细描述, image varchar(255) DEFAULT NULL COMMENT 主图URL, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0待审核 1待认养 2已认养 3已下架, owner_id bigint DEFAULT NULL COMMENT 送养人ID, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 发布时间, PRIMARY KEY (id), KEY idx_status (status), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT猫咪信息表;认养申请表是连接用户和猫咪的桥梁也是整个系统最核心的状态流转表CREATE TABLE adoption_application ( id bigint NOT NULL AUTO_INCREMENT COMMENT 申请ID, user_id bigint NOT NULL COMMENT 认养人ID, cat_id bigint NOT NULL COMMENT 猫咪ID, reason varchar(500) DEFAULT NULL COMMENT 认养理由, contact_info varchar(100) DEFAULT NULL COMMENT 联系方式, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0待审核 1已通过 2已拒绝, review_remark varchar(255) DEFAULT NULL COMMENT 审核备注, reviewer_id bigint DEFAULT NULL COMMENT 审核人ID, review_time datetime DEFAULT NULL COMMENT 审核时间, apply_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 申请时间, PRIMARY KEY (id), KEY idx_cat (cat_id), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT认养申请表;三个设计细节我想重点说一下。第一个是status字段的状态值设计。猫咪的状态我分了0待审核、1待认养、2已认养、3已下架四种。审核通过一个认养申请时需要同时把猫咪的status改成已认养才能保证“一只猫只能被认养一次”这个业务规则成立。这个逻辑如果不在代码里控制后面会出现一个猫咪被两个人认养的数据脏问题。第二个是外键的处理。我刻意没有在数据库层加物理外建而是在Java代码里用id做关联查询。原因很实际毕设项目经常要插入测试数据或做页面调试物理外键会带来一大堆约束麻烦同时MyBatis-Plus本身支持逻辑关联不需要数据库帮你维护一致性。第三个是时间字段。我统一用datetime不用timestamp。timestamp有时区陷阱尤其在部署到云服务器后发现时间差了8个小时排查时间比想象中长得多。datetime没有时区问题省心。2.3 初始化脚本里的学问很多同学的SQL脚本只有建表语句这没问题但如果能把初始化和测试数据一起给出来别人拿到项目打开就能跑体验会完全不一样。我把脚本拆成了两份01_schema.sql和02_data.sql前者建库建表后者插入基础数据。基础数据至少包含两部分一个初始管理员账号用户名admin密码用BCrypt加密后的字符串以及若干条猫咪测试数据覆盖不同的品种、状态。这里有个很实用的技巧为了演示方便我故意造了两只不同状态的猫咪——一只“待认养”一只“已认养”这样打开前端就能看到状态标签的对比效果。另外字符集一定要用utf8mb4不要省事用utf8。原因在于猫咪描述文本里可能包含emoji表情utf8存不进去插入记录时直接报错我第一次就是没注意这个跑到一半才发现数据库根本写不进带表情的描述。3. 接口文档与后端实现先定义返回格式再做业务代码3.1 接口文档的组织方式从Apifox到离线Markdown项目标题里带“接口文档”意味着交付物里这是单独一份。我刚开始用Swagger自动生成但发现Swagger的文档对前端联调并不友好尤其是请求参数示例不够直观。后来换成Apifox调试接口的同时就能维护文档最后直接导出成离线Markdown文件放进交付目录。接口文档里我固定写清楚五件事请求地址、请求方式、请求参数说明名称/类型/是否必填/说明、请求示例、响应示例。举个例子接口方法路径说明用户注册POST/api/auth/register提交用户名、密码、昵称、手机号用户登录POST/api/auth/login返回JWT Token和用户信息分页查询猫咪GET/api/cats/page支持品种、状态筛选返回分页结构猫咪详情GET/api/cats/{id}返回猫咪完整信息提交认养申请POST/api/adoptions/apply用户提交认养理由和联系方式我的申请列表GET/api/adoptions/my返回当前用户的申请记录审核认养申请PUT/api/admin/adoptions/{id}/review管理员通过或拒绝申请发布公告POST/api/admin/announcements管理员新增公告文件上传POST/api/file/upload返回图片访问URL接口文档先于后端代码写完善有一个实际好处前端页面需要的字段、后端需要提供的字段在一开始就能对齐后面联调阶段几乎不会出现“前端拿不到某个字段”然后临时改接口的情况。3.2 统一响应结构与JWT认证的实现后端设计的第一个公共组件是统一响应结构Result。所有接口都返回固定格式code、msg、data。这个设计看起来简单但实际开发中非常关键。前端axios拦截器只需要判断一次code不用每个接口各自处理异常分支。Data public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(200); r.setMsg(success); r.setData(data); return r; } public static T ResultT error(Integer code, String msg) { ResultT r new Result(); r.setCode(code); r.setMsg(msg); return r; } }认证方案我没有上Spring Security而是选择了JWT加自定义拦截器。原因很直接Spring Security的过滤器链对初学者来说是黑盒出了问题不好排查答辩时也很难三言两语讲清楚。JWT的思路是所有访问受保护接口的请求都带一个Authorization: Bearer token的请求头后端拦截器解析token校验通过后放行同时把用户id存入当前请求上下文。Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equals(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { Long userId JwtUtil.parseToken(token.replace(Bearer , )); if (userId ! null) { UserContext.set(userId); return true; } } response.setStatus(401); response.getWriter().write({\code\:401,\msg\:\未登录或登录已过期\}); return false; } }注册登录时密码用BCrypt加密存储不要存明文。这是答辩时老师大概率会问的“安全措施”问题之一提前在代码里做了回答起来就有底气。3.3 核心接口的实现细节分页、状态流转与文件上传猫咪列表的分页查询用MyBatis-Plus的分页插件。这里要手动配置一个MybatisPlusInterceptor把PaginationInnerInterceptor加进去否则selectPage不会生效Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }查询接口支持按品种和状态筛选GetMapping(/page) public ResultIPageCat page(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String breed, RequestParam(required false) Integer status) { LambdaQueryWrapperCat wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(breed), Cat::getBreed, breed) .eq(status ! null, Cat::getStatus, status) .orderByDesc(Cat::getCreateTime); return Result.success(catMapper.selectPage(new Page(pageNum, pageSize), wrapper)); }认养申请的状态流转是核心中的核心。提交申请时需要同时判断猫咪当前状态必须是“待认养”否则直接提示“该猫咪已被认养或不可申请”。审核通过时除了更新申请状态为已通过还要把猫咪状态更新为已认养并且建议把其他未审核的申请自动置为已拒绝避免后续用户拿一只已经被认养的猫继续申请。Transactional Override public void review(Long applicationId, Integer status, String remark) { AdoptionApplication app applicationMapper.selectById(applicationId); if (app null || app.getStatus() ! 0) { throw new BusinessException(申请不存在或已处理); } app.setStatus(status); app.setReviewRemark(remark); app.setReviewTime(new Date()); applicationMapper.updateById(app); if (status 1) { Cat cat catMapper.selectById(app.getCatId()); cat.setStatus(2); catMapper.updateById(cat); // 拒绝该猫咪的其他待审核申请 applicationMapper.rejectOtherPending(app.getCatId(), app.getId()); } }关于文件上传我建议图片保存到服务器本地指定目录然后返回一个可通过nginx或SpringBoot静态资源映射访问的URL。注意保存路径要用配置项管理别写死在代码里的绝对路径不然换一台电脑路径就不对了。3.4 LocalDateTime序列化这个坑一定要提前处理我第一次写完接口前端拿到的时间字段长这样createTime: [2025, 3, 15, 12, 30, 45]。原因是Jackson默认把LocalDateTime序列化成数组了前端拿到手根本没法直接用。解决办法有两种要么在字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)要么在配置文件里全局处理spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8全局配置只对java.util.Date生效对LocalDateTime还是不行所以我最终在统一配置类里注册了JavaTimeModule的序列化器一次性解决所有LocalDateTime的格式问题。这个坑非常典型很多毕设项目都是在联调阶段突然卡住的。4. Vue3前端的搭建路由、请求封装与页面落地4.1 初始化前端项目与目录划分前端我用npm create vitelatest创建了Vue3项目选择JavaScript而不是TypeScript。毕设场景下JS更省事不需要处理类型定义写起来快很多。目录结构按业务模块划分src/ ├── api/ // 每个模块的接口调用文件 ├── router/ // 路由配置 ├── stores/ // Pinia状态管理用户信息 ├── views/ // 页面组件 │ ├── Home.vue // 首页 │ ├── CatList.vue // 猫咪列表 │ ├── CatDetail.vue // 猫咪详情 │ ├── Login.vue │ └── admin/ // 后台管理页面 ├── components/ // 公共组件 └── utils/ // 请求封装、工具函数目录划分这件事比大多数人想象的重要。答辩时老师可能会直接问你“项目结构怎么组织的”你把目录说清楚比背一段八股文更有说服力。4.2 路由配置与登录守卫路由必须区分游客和管理员。我在路由的meta里标记是否需要登录和需要的角色const routes [ { path: /, component: Home }, { path: /cats, component: CatList }, { path: /cats/:id, component: CatDetail }, { path: /login, component: Login }, { path: /admin, component: AdminLayout, meta: { requiresAuth: true, role: admin } } ]再配合全局前置守卫做登录控制router.beforeEach((to, from, next) { const userStore useUserStore() if (to.meta.requiresAuth !userStore.isLoggedIn) { next(/login) } else if (to.meta.role to.meta.role ! userStore.role) { next(/) } else { next() } })这里有个设计细节管理员的路由地址我用/admin前缀统一管理和用户端完全分开入口在首页右上角只有管理员登录后才显示。这样前端结构清晰后端也方便用拦截器对/api/admin/**做权限校验前后端的“角色分层”是一致的。4.3 Axios请求封装与Token处理axios封装是前端联调的关键。我统一创建了一个axios实例配置baseURL为/api然后在请求拦截器里自动带上token响应拦截器里统一处理code非200的情况import axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) service.interceptors.response.use(res { const result res.data if (result.code 200) { return result } if (result.code 401) { localStorage.removeItem(token) window.location.href /login return Promise.reject(new Error(未登录)) } ElMessage.error(result.msg || 请求失败) return Promise.reject(new Error(result.msg)) }, err { ElMessage.error(网络异常) return Promise.reject(err) })统一封装之后页面里调接口只需要维护src/api/cat.js这样的文件每个方法对应一个后端接口export function getCatPage(params) { return service.get(/cats/page, { params }) } export function getCatDetail(id) { return service.get(/cats/${id}) } export function applyAdoption(data) { return service.post(/adoptions/apply, data) }4.4 核心页面的实现要点猫咪列表页用的是Element Plus的el-card栅格布局每个卡片展示猫咪主图、昵称、品种、状态标签。状态标签是列表页的视觉重点我根据后端返回的status数字动态映射成不同的标签颜色0待审核灰色、1待认养绿色、2已认养橙色、3已下架红色。这个映射在前端做一个纯函数就行但能明显提升页面质量。猫咪详情页要展示完整信息下面的认养申请表单是重点。提交之前前端先判断用户是否登录未登录就跳转登录页。表单字段包括认养理由和联系方式提交成功后提示“申请已提交等待管理员审核”并按钮置灰防止重复提交。后台管理页面我用了el-table加el-dialog的组合。猫咪管理表格支持分页、搜索、编辑、删除编辑弹窗里用el-upload上传图片。认养审核页面是表格展示所有申请点击审核弹窗选择通过或拒绝通过时后端会联动更新猫咪状态。这里有个值得留意的体验细节管理后台和用户端的登录状态要共用同一个Pinia store和同一个localStorage token。我一开始没注意后台接口用的独立axios实例结果管理员登录后后台页面还是提示未登录排查了半天发现是两个实例没有统一携带token。后来统一用一个请求封装问题立刻消失。4.5 dev代理配置开发环境跨域的正解开发时前端跑在5173端口后端跑在8080端口直接请求会有跨域问题。前端这边我通过Vite的proxy配置解决// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这样前端代码里写的都是相对路径/api/xxx浏览器请求同源Vite开发服务器把它转发到后端。再加上后端配置CORS允许来源两条路都通了。生产环境部署时前端请求也会经过nginx反向代理到后端接口整个链路的理解是一致的。5. 联调、打包与部署把项目真正跑起来的完整记录5.1 后端CORS配置与开发环境联调虽然Vite已经在开发层解决了跨域但我在后端还是加上了CORS全局配置。原因很简单万一有人不用Vite的proxy直接用IP加端口访问后端跨域问题是必然的。后端配置文件里的方式是最稳妥的Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }注意allowedOriginPatterns不要写allowedOrigins(*)和allowCredentials(true)同时用后者会被浏览器拦截。这个问题我当时查了不少时间最后发现是旧写法在新版本浏览器里不兼容。5.2 生产部署的两种常见姿势毕设项目的部署方式我总结了两种主流做法我都试过各有适用场景。第一种是前后端分离部署SpringBoot打包成jar用java -jar运行Vue打包成dist目录交给nginx托管nginx再配一个/api反向代理到后端端口。这种方式最接近真实项目结构面试聊起来也是加分项。第二种是把前端打包产物直接放进SpringBoot的src/main/resources/static目录下整个项目打成一个jar双击就能跑。这种方式特别适合演示日那种“打开电脑就要跑起来”的场景不用单独装nginx。但要注意如果前端路由用了history模式直接访问刷新会404需要在后端加一个转发规则。我在application.yml里配置了静态资源映射把上传的图片目录也暴露出来spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB resources: static-locations: classpath:/static/,file:${upload.path}打包的时候注意一件事前后端要分开构建别在前端dist生成之前就迫不及待地打成jar。我踩过一次打出来的jar里static目录是空的前端页面全404。正确的顺序是先npm run build生成dist把dist内容拷贝到后端static目录再Maven打包。5.3 我踩过的坑与排查思路汇总整个项目做完我把遇到频率最高的几个问题整理成了一张表这些问题在别人跑我的源码时大概率也会碰到报错或现象根本原因解决办法连接MySQL报CommunicationsException没有配置时区参数JDBC URL加上serverTimezoneAsia/ShanghaiLocalDateTime变成数组Jackson序列化LocalDateTime默认行为注册JavaTimeModule并指定日期格式前端请求全部401拦截器放行了OPTIONS但前端没带token检查请求头名称是否一致统一Bearer前缀分页不生效返回全量数据MyBatis-Plus分页插件没注册配置MybatisPlusInterceptor添加PaginationInnerInterceptor图片上传成功但访问404静态资源映射没配置上传目录spring.resources.static-locations加上file路径前端刷新页面404Vue history模式需要服务端支持nginx配置try_files或后端兜底转发到index.html同一个猫咪被两个申请同时通过审核接口没做状态判断和并发控制审核前判断cat.status事务内更新状态用Transactional排查思路比答案更重要。第一次遇到404别急着去改代码先分清楚是“接口404”还是“页面404”。然后拿Postman直接打后端接口后端通了再怀疑前端前端报错先看控制台再看网络请求的响应体。逐层缩小范围比瞎改要快很多。5.4 答辩前最容易被问到的几个技术问题项目做完之后我专门准备了几道高频答辩问题我发现老师对毕设项目的提问基本集中在“为什么这么选”和“这里怎么实现”两个方向。为什么用JWT不用Session我的回答思路是JWT无状态后端不需要存储会话信息适合前后端分离架构登录成功后前端持有token每次请求带上后端通过签名校验身份。对比Session需要依赖服务端内存和Cookie传输在API接口场景下JWT更自然。认养状态的一致性怎么保证我的回答思路是提交申请时检查cat.status审核通过时在同一个事务里更新申请状态和猫咪状态其他待审核的申请自动拒绝。数据库层面再通过索引保证同一猫咪的申请可查询避免并发重复通过。如果用户量大了怎么办这个问题很常见不一定真的要实现但要能说出思路。我的回答是当前用的MyBatis-Plus分页是物理分页后续可以把热门猫咪缓存到Redis把文件存储迁移到对象存储接口层面加统一限流。能说出这些词至少说明你想过扩展方向。最后再说几句大实话项目全部整理完后我把整个交付目录按照毕设要求重新整理了一遍sql脚本、接口文档、后端完整源码、前端完整源码、README说明文件。README里写清楚了环境版本、启动步骤、初始账号密码我的体会是一个毕设项目交付质量的判断标准往往不是代码量多豪华而是另一个人拿到你的项目之后能不能在半小时内跑起来。能做到这一点你的项目就是合格的。如果你现在正卡在某个环节建议先从数据库和接口文档抓起这两个东西定了前后端就只剩下填代码了。祝顺利。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑