SpringBoot+Vue作家信息管理系统:从数据库设计到前后端联调全解析

SpringBoot+Vue做管理系统,大概是Java Web方向毕业设计里最常见的一类题目了。但同样是“管理系统”,有的只能做到增删改查,有的却能作为面试作品拿出来讲。差别在哪?就在数据建模、接口设计、异常处理、前后端联调这些细节里。

这套“SpringBoot+Vue当代中国获奖知名作家信息管理系统”,是个典型的全栈Java Web项目。麻雀虽小,五脏俱全,正好能把SpringBoot后端、Vue前端、MySQL数据库、接口文档这些东西串成一条完整的链路。下面我就以这套系统为底子,从项目结构到核心代码,把所有关键细节拆开讲透,最后再聊我在实操里踩过的坑和调试经验。不管是拿去做毕设还是刚学完框架想练手,这篇文章的项目设计思路和代码实现都值得你跟着完整走一遍。

1. 项目整体拆解:为什么是SpringBoot+Vue

1.1 技术选型背后的真实逻辑

不少同学会纠结:管理系统到底用JSP+Servlet行不行,或者用Thymeleaf行不行?当然行,但时代变了。SpringBoot解决了传统SSH/SSM项目里大量的XML配置和容器部署问题,内嵌Tomcat,一个jar包直接跑起来;Vue通过组件化和状态管理,把页面拆成一个个独立单元,多人协作互不干扰。前后端通过JSON格式数据通信,接口解耦,谁修改谁不影响对方。

这套作家信息管理系统,后端就是标准的SpringBoot 2.x工程,前端是Vue 2.x + Element UI。前后端一共就两个进程:后端跑在8080端口,前端开发时跑在8081端口走代理,打包后由后端统一托管静态资源。

你把这个选型逻辑讲清楚,答辩的时候就不只是“我用了什么框架”,而是“我为什么这么选”。

1.2 系统模块划分

按当下比较常见的毕设要求,系统拆成两个角色:管理员端和访客展示端。管理端功能围绕作家信息的全生命周期管理展开,具体包括:

  • 作家基础信息管理:录入姓名、笔名、籍贯、出生日期、文学流派、代表作数量等,支持头像上传。
  • 作品信息管理:针对某作家维护其代表作,包含作品名称、类别、出版年份、获奖情况。
  • 获奖记录管理:记录作家获得的奖项名称、年份、颁奖单位、级别(国际/国家级/省级等)。
  • 作家检索与列表分页:支持按姓名、流派、获奖级别、获奖年份等维度进行组合检索。
  • 分类展示:按文学流派(小说、诗歌、散文、儿童文学等)和获奖级别进行分类聚合。
  • 系统管理扩展:简单的管理员账号管理。

访客展示端就是只读的:看作家列表、看作家详情、看获奖履历,用卡片式布局展示,数据来自后端接口。整体功能不复杂,但胜在链路完整,从建库建表到接口编写再到页面联调一次性串完。

1.3 数据库要先把根扎稳

系统叫“信息管理系统”,本质上玩的就是数据。数据库设计是整个项目里最不能糊弄的部分。这套系统沿用了经典的三张核心表结构:作家表(author)、作品表(work)、获奖记录表(award),外加一张管理员表(admin_user)。

其中,作家与作品是一对多关系,作家与获奖记录也是一对多关系。主外键都用逻辑外键,也就是只在代码里维护关联,而不是在MySQL里强行加物理外键约束。为什么这么做?物理外键在数据导入导出和批量删除时会增加一堆限制,即使现在看起来“安全”,后续如果搞性能优化,这些外键非常碍事。实际参与项目后你慢慢就会发现,业界主流做法就是逻辑外键加索引,不用物理外键。

2. 核心需求与数据建模

2.1 作家信息对象的字段设计细节

作家信息是整个系统的核心实体。字段设计得合理,后面所有功能都顺畅。这套系统的author表字段如下:

字段名类型说明
idbigint(20) 自增主键
author_namevarchar(50)姓名
pen_namevarchar(50)笔名,可空
gendertinyint(1)性别(0未知,1男,2女)
birth_datedate出生日期
native_placevarchar(100)籍贯,省/市
genrevarchar(30)文学流派,如小说、诗歌
educationvarchar(50)学历背景,可空
profiletext个人简介,可长文本
avatar_urlvarchar(255)头像图片地址
statustinyint(1)启用状态,0停用1启用
create_timedatetime创建时间
update_timedatetime更新时间

我特别提两个点。

第一个是gender用tinyint而不是varchar。用int类型存数值,后续如果要在页面上做图表统计(男/女作家占比),一个GROUP BY就能出结果。这比存“男”“女”字符串高效,也可以避免中文编码不一致的问题。

第二个是intro和profile这类长文本字段,一定要用text或longtext。有些同学喜欢什么字段都上varchar(255),作家简介稍微写长一点,数据库直接报错或者截断,体验很糟糕。

2.2 作品表与获奖表的关联建模

作品表work核心就几个字段:id、author_id、work_name、work_type(长篇小说/中篇小说/短篇小说/散文集/诗集)、publish_year、publisher(出版社)、award_ids(可空扩展)和description。

这里设计时有一个容易想多的地方:作品和奖项要不要做多对多关联表?比如一本书拿了茅盾文学奖,又拿了某省“五个一工程奖”,是不是得建一张作品-奖项中间表?

从工程角度说,如果项目周期只有三个月,这样做会让复杂度直接翻倍。更务实的做法是:作品表里只保存description字段做文字描述;独立的获奖记录表,严格关联到作家,而不是作品。也就是说,writer_info级联查询获奖记录。原因很简单:访客端展示的重点是“这个作家获得过什么奖”,而不是“这本书拿过什么奖”。在毕设或demo项目里,一定要克制建模冲动,按真实使用场景来,不然就是拿生产级复杂度折磨自己。

获奖记录表相对独立,核心字段是award_id、author_id、award_name、award_level(国际级/国家级/省部级/其他)、award_year、issuing_org(颁奖单位)。这里有个小技巧,award_year用int类型而不是date类型,因为大部分奖项只需要年份,精确到月份反而增加录入成本。

2.3 常见里容易被忽略的枚举设计

你可能注意到了,很多字段本质上是“枚举”:文学流派、作品类型、获奖级别、性别。这些字段我是怎么处理的?

我倾向在一个全局配置类里定义常量或枚举类,而不是在数据库里建字典表。比如文学流派就那几种,和一个大而全的字典表相比,写在Java枚举里,编码时有提示,校验也方便,前端下拉框选项也从接口里返回。只有在字段未来会频繁扩展时才考虑建字典表。

具体实现上,可以写一个枚举类,比如AwardLevelEnum,上有levelName和displayName,后端接收接口转换成int,在VO层再转换成可读名称返回给前端。好处是数据表里存的是数字,干净省空间,展示层想翻译成什么语言都行。

3. 后端核心实现:从Controller到数据库访问

3.1 分层架构与包结构

后端工程是按标准分层架构组织的,包名可以按下面这样定义:

com.example.writer ├── controller // 前端接口入口 ├── service // 业务逻辑层 │ └── impl ├── mapper // MyBatis接口 ├── entity // 数据库实体 ├── vo // 视图对象,组合返回给前端 ├── common // 通用返回体、异常处理、常量 ├── config // 跨域、文件上传等配置 └── util // 工具类

这套结构的核心思想是:Controller只做参数接收和结果返回,不碰数据库;Service只做业务规则,不写SQL;Mapper层只做持久化操作。一旦分层清晰,后续出问题定位很快:返回数据不对查Controller和VO,业务逻辑错查Service,SQL慢查Mapper。

3.2 统一返回体设计

很多同学写接口是各返回各的,有的返回Map,有的直接返回List,有的成功时不返回任何内容。前端拿到数据之后都不知道怎么判断成功与否。这套系统里,我设计了统一的响应包装类Result返回给前端。

Result结构非常简单,标准的三段式:

public class Result<T> { private Integer code; private String msg; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMsg("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(String msg) { Result<T> result = new Result<>(); result.setCode(500); result.setMsg(msg); return result; } }

这里的code字段非常关键。HTTP状态码是传输层的语义,业务状态码是业务层语义。比如参数校验不通过,HTTP仍然是200,但业务code返回40001,前端就能精确定位是哪一类问题。同时配合全局异常处理器@RestControllerAdvice,把异常、校验失败、业务错误统一转成Result格式,这样Controller里的代码可以非常清爽,不需要每个接口都写try-catch。

3.3 分页与多条件检索

作家管理页面的核心就是一个带分页的多条件检索列表:按姓名模糊查询、按流派精确匹配、按获奖级别筛选、按获奖年份范围查询。这里用MyBatis的方式处理。

分页直接用PageHelper插件,引入依赖后一行代码搞定:

<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.7</version> </dependency>

Service层是这样用的:

public PageResult<AuthorVO> getAuthorList(AuthorQuery query) { PageHelper.startPage(query.getPageNum(), query.getPageSize()); List<AuthorVO> list = authorMapper.selectByCondition(query); PageInfo<AuthorVO> pageInfo = new PageInfo<>(list); // 组装成带total的PageResult对象返回 }

需要注意一个坑:PageHelper一旦设置了分页,紧接着执行的第一条SQL会被自动加上limit。如果你在startPage和mapper调用之间插入了其他查询,分页就会加到那条无关查询上,数据就乱了。所以一定要让startPage紧贴mapper查询方法。

Mapper里的动态SQL有条件判断,意思是某个参数为空就不拼这个条件:

<select id="selectByCondition" resultType="com.example.writer.vo.AuthorVO"> SELECT a.*, (SELECT COUNT(*) FROM work w WHERE w.author_id = a.id) AS work_count FROM author a <where> <if test="authorName != null and authorName != ''"> AND a.author_name LIKE CONCAT('%', #{authorName}, '%') </if> <if test="genre != null and genre != ''"> AND a.genre = #{genre} </if> </where> ORDER BY a.update_time DESC </select>

这里子查询算作作品数,列表上就能直接展示“代表作×N”,而不用在Java代码里循环查一遍。少一次N+1查询,性能也更好。

3.4 头像上传:文件存哪里

头像上传是个经典需求。正路是后端接收MultipartFile,保存到服务器磁盘的指定目录,再把访问URL存进数据库。

@PostMapping("/upload") public Result<String> uploadAvatar(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return Result.error("上传文件不能为空"); } String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); // 生成唯一文件名,防止重名覆盖 String newFileName = UUID.randomUUID().toString().replace("-", "") + suffix; // 按日期分目录存储,避免一个目录文件过多 String dateDir = new SimpleDateFormat("yyyyMMdd").format(new Date()); File dir = new File(UPLOAD_PATH + dateDir); if (!dir.exists()) { dir.mkdirs(); } file.transferTo(new File(dir, newFileName)); // 返回给前端的访问路径 return Result.success("/upload/" + dateDir + "/" + newFileName); }

保存路径与访问路径分离。原则上来讲,磁盘路径对外绝不可暴露,不然等于把服务器目录结构公之于众。前端存储的是访问URL,后台上传时映射静态资源目录,这样用户通过浏览器访问URL,拿到的是文件二进制数据,而不是服务器的真实路径。

如果是线上环境,更专业的做法是存到对象存储服务(比如阿里云OSS或MinIO),上传成功后拿到一个CDN加速URL。但毕设或本地demo阶段,本地磁盘方案已经足够,而且实现起来更直观。

4. Vue前端:从零搭一个作家管理后台

4.1 前端项目结构与路由配置

前端用Vue CLI搭建,核心目录结构:

src ├── api // axios请求封装 ├── assets // 静态资源 ├── components // 公共组件 ├── router // 路由配置 ├── store // Vuex状态管理(登录态等) ├── views │ ├── admin // 后台管理页面 │ ├── home // 前台展示页面 │ └── login // 登录页 └── main.js

路由这块,最好设置懒加载:

const routes = [ { path: '/', name: 'Home', component: () => import('../views/home/HomePage.vue'), meta: { title: '作家展示首页' } }, { path: '/admin', component: () => import('../views/admin/AdminLayout.vue'), children: [ { path: 'authors', component: () => import('../views/admin/AuthorList.vue'), meta: { title: '作家管理' } } ] } ];

懒加载的意义不仅是首次加载更快,它会把每个路由的代码独立拆成chunk文件,访问到哪个页面才加载哪个页面。对后台管理这类页面较多的项目,提升很明显。

4.2 Axios封装与请求拦截

前端所有请求都要带上后端返回的token,以及统一处理后端的code码。通常在src/api目录建一个request.js:

import axios from 'axios'; import { Message } from 'element-ui'; import router from '../router'; const request = axios.create({ baseURL: process.env.VUE_APP_BASE_URL || 'http://localhost:8080/api', timeout: 10000 }); // 请求拦截器:自动带上token request.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers['Authorization'] = token; } return config; }); // 响应拦截器:统一处理返回体 request.interceptors.response.use( response => { const res = response.data; if (res.code !== 200) { Message.error(res.msg || '请求失败'); if (res.code === 401) { router.push('/login'); } return Promise.reject(new Error(res.msg)); } return res.data; }, error => { Message.error(error.message || '网络异常'); return Promise.reject(error); } ); export default request;

封装好之后,接口调用就非常干净了。比如作家列表接口:

export function getAuthorList(params) { return request({ url: '/author/list', method: 'get', params }); }

这里有个很容易被忽略的点:baseURL里的/api。后端Controller的映射路径如果是/author/list,前端请求/baseURL/author/list,那后端就需要在统一前缀里加/api。两种方式都能用,但整个项目内部必须统一。不然就会出现一半接口能调通、一半接口报404的诡异情况。

4.3 作家列表页与弹窗表单

列表页是后台系统的门面,功能要完整:条件检索、分页、新增、编辑、删除、头像显示。布局用Element UI的el-card包el-form和el-table。

表格的列设计要和后端VO对应起来。作家列表字段较多,我会把简介和头像这样的字段单独处理:头像固定在左侧用el-avatar显示;简介超30个字符截断,鼠标悬浮用el-tooltip显示全文。

新增和编辑共用同一个弹窗组件,通过editId来区分。打开弹窗时,如果editId为空就调用resetFields清空表单,否则调用detail接口回显数据。这里有一个很容易踩的坑:el-form的resetFields只能重置到初始值,而初始值是定义在data里的。如果只在打开新增弹窗时调用一次resetFields,第二次打开时上一次的数据可能清不干净。正确做法是:每次打开弹窗时,用this.$nextTick配合setTimeout重置,或者在弹窗外层用v-if,关闭时将组件彻底销毁。我建议直接用v-if方案,简单粗暴,绝不会残留脏数据。

4.4 前台展示页与接口适配

前端展示页和服务端管理端的主要区别在于:展示数据是只读的,布局偏卡片化,要能体现设计师审美。在首页,可以做一个作家瀑布流布局:el-row分两列,每张卡片展示头像、姓名、流派标签、获奖简介和“查看详情”按钮。

详情页通过路由参数接收作家ID,调用后端详情接口,返回的是聚合好的VO对象(包含作者基本信息、作品列表、获奖列表)。前端直接渲染三个Tabs即可。

这里有个关键点:详情接口返回的是一个聚合VO,而不是三个接口。如果前端分别发起三个请求,一是多出网络开销,二是增加了前端的组装逻辑。后端一次性返回聚合数据,做的是数据库联合查询或三次查询后在Service层组装,这块逻辑离数据库更近,性能可控,前端代码也更简洁。

5. 接口文档与SQL脚本:工程化的最后一环

5.1 接口文档写什么、怎么用

接口文档在团队协作里是“契约”。前后端能不能顺畅配合,很大程度上取决于接口文档写得好不好。如果你连接口文档都没有,后端传一个date类型字段,前端默认按string处理,格式化时直接显示“2024-05-06T16:00:00.000Z”,用户一看就懵了。

接口文档至少要包含下面这些信息:

接口名称请求方式路径参数说明返回示例
作家分页列表GET/api/author/listpageNum, pageSize, authorName, genre, awardLevel{code:200, data:{total:100, list:[...]}}
作家详情GET/api/author/detail/{id}path参数{code:200, data:{authorVO}}
新增作家POST/api/author/addJSON格式AuthorDTO{code:200, msg:"新增成功"}
修改作家PUT/api/author/updateJSON格式AuthorDTO{code:200, msg:"修改成功"}
删除作家DELETE/api/author/delete/{id}path参数{code:200, msg:"删除成功"}
头像上传POST/api/uploadmultipart/form-data file字段{code:200, data:"/upload/xxx.jpg"}

返回示例必须真实可执行。你把文档写清楚了,前端对接时不需要反复问“这个字段什么意思”,直接拷贝示例数据就能把页面渲染出来,联调效率能提一倍。

如果想省事,也可以引入Swagger或Knife4j自动生成接口文档。但我个人建议毕设这档子事,手写一份Markdown接口文档放在项目docs目录下,反而更显用心。文档里再加几个“注意事项”,比如时间字段的格式是yyyy-MM-dd,前端组件要用datePicker配合value-format,都是加分项。

5.2 SQL脚本的完整交付清单

一个合格的SQL脚本,不是把创建表语句扔给你就完事了。它应该包括:

  • 建库语句:CREATE DATABASE IF NOT EXISTS writer_system DEFAULT CHARACTER SET utf8mb4;
  • 建表语句:所有表的CREATE TABLE,带注释;
  • 初始化数据:插入几条示例作家、作品、获奖记录,至少让系统一启动就能看到效果;
  • 索引语句:在常用查询字段上建索引。

初始化数据特别重要。如果SQL脚本不包含数据,前端人员拿到空库,页面一片空白,他得手动去后台录入才能看到效果,体验极差。而预置个三五个作家,每个作家两三部作品、两条获奖记录,页面一下子丰满起来。

表引擎直接用默认的InnoDB,字符集必须指定utf8mb4。很多人用utf8,但utf8在MySQL里最多存3个字节,一些特殊字符(比如生僻字、emoji)会存不进去。作家姓名里有生僻字是常有的事,别在这个地方出问题。

5.3 索引设计:别忽略查询性能

虽然数据量可能只有几百条,索引有没有用体感不强,但这是个很好的答辩点。梳理一下查询场景:

  • 按照作者姓名模糊查询,需要建索引吗?模糊查询LIKE '%xxx%',以通配符开头时索引会失效,但LIKE 'xxx%'可以用索引,所以视实现而定。
  • 按流派精确匹配:genre字段建普通索引。
  • 按获奖级别筛选:award表award_level建普通索引。
  • 外键关联字段:work.author_id、award.author_id建普通索引,这也是最常规的。
ALTER TABLE author ADD INDEX idx_author_name (author_name); ALTER TABLE author ADD INDEX idx_genre (genre); ALTER TABLE work ADD INDEX idx_author_id (author_id); ALTER TABLE award ADD INDEX idx_author_id (author_id); ALTER TABLE award ADD INDEX idx_award_level (award_level);

索引不是越多越好。它加快读操作,但每次增删改都要同步维护索引,拖慢写操作。我们没有在text字段上建索引,也没有在低区分度的gender上建索引,都是基于实际场景的取舍。

6. 部署调试与常见问题排查

6.1 本地连不上MySQL时的处理顺序

后端启动报Access denied for user或者Communications link failure,90%出在配置上。按下面这个顺序排查:

一、看application.yml里的地址和端口有没有写错,最常见的是把localhost写错,或者MySQL端口不是默认3306。 二、看用户名密码是否正确。本地一般用root,密码别带多余的空格。 三、看MySQL是否创建了对应的库:SHOW DATABASES;,没建库就先执行SQL脚本。 四、特别容易忽略的是时区问题。连接串推荐写成:

spring: datasource: url: jdbc:mysql://localhost:3306/writer_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true

serverTimezone=Asia/Shanghai这个参数,不加的话高版本MySQL驱动会报时区错误。allowPublicKeyRetrieval=true是因为MySQL 8.x默认使用caching_sha2_password插件,本地连接时可能触发公钥检索报错。这两个参数加好,能省掉很多折腾。

6.2 跨域问题:Preflight请求404

前端在8081端口开发,后端在8080端口,浏览器会发起跨域请求。如果后端不做跨域处理,就会出现“请求已发送但拿不到响应”的情况。专业点说是预检请求(OPTIONS)没通过。

后端统一配置跨域即可:

@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); } }

这里注意,allowedOrigins("")和allowCredentials(true)不能同时使用,要么指定具体来源,要么用allowedOriginPatterns("")。这是Spring的规范,写错了哪怕配置了也会在运行时抛异常。

6.3 集成测试与演示数据准备

项目能跑通只是第一步,怎么让它演示效果好才是重中之重。我强烈建议在开发阶段就准备一套“教科书级”的演示数据。

比如某位“作家”信息:不到五十岁,已出版长篇五部,拿过两项国家级奖项,简介写得既有文学性又有数据感。系统一打开,展示页上琳琅满目,评委一眼就能看到所有功能点。

演示时还有一个常见的要提前准备的场景:删除某个作家时,如果他下面关联了作品和获奖记录,是直接报错,还是级联删除?我建议删除前做个判断,如果有作品或获奖记录,提示先删除关联数据。既展示了你对数据完整性的考虑,也为面试时“你是怎么设计删除逻辑的”准备好了答案。

6.4 一个容易忽视的“日期格式化”问题

前端提交出生日期时,传来的是一个“yyyy-MM-dd”的字符串,后端用Date类型接收,如果不做任何配置,Spring默认解析格式是“yyyy/MM/dd”,直接解析失败。

后端加@JsonFormat注解能解决部分问题,但更推荐在application.yml里全局配置:

spring: jackson: date-format: yyyy-MM-dd time-zone: GMT+8

这样JSON序列化和反序列化都会统一按这个格式处理,省去在几十个字段上逐个加注解的繁琐操作。如果使用了LocalDateTime,还需要额外引入jackson-datatype-jsr310模块,Spring Boot一般会自动装配,但要确认引了对应的starter。

6.5 熟悉问题速查表

把实操中常见的问题整理成一张表,方便定位:

现象原因解决方案
后端启动报端口占用8080端口被其他进程占用改配置或杀进程,lsof -i:8080
前端请求不到后端接口跨域未配置或baseURL错误检查CorsConfig和请求地址
数据库中文乱码数据库/表未用utf8mb4建库建表指定utf8mb4,连接串加characterEncoding=utf8
日期字段显示2024-05-06T16:00:00.000Z后端返回Date且时区UTCjackson统一配置date-format和GMT+8
图片上传后访问404静态资源映射未配置实现addResourceHandlers映射/upload/**到磁盘
MyBatis报了语法错误XML里的<符号引起XML解析问题用&lt;转义或写进CDATA区块
分页后总条数不对PageHelper线程安全问题确认startPage紧跟查询,SELECT和count和list在同一条线程

7. 这个项目还能怎么扩展

如果做完基础功能还有余力,下面几个方向能让整个系统的完成度和技术深度再上一个台阶。

第一,引入Redis缓存作家列表。热点数据(比如首页展示的作家列表)缓存到Redis,查询时先走缓存,写操作后删除缓存或更新缓存。做好了,这就是“缓解热点数据压力”的经典实践,能在答辩中讲五分钟。

第二,用Vuex或Pinia管理用户登录态。把管理员登录的token放到请求头里,后端写一个登录拦截器,没登录就返回401,前端跳回登录页。这套逻辑做完,整个项目的流程就完整了,不再是“裸奔”的系统。

第三,加一个“获奖等级分布”图表。后端提供一个聚合接口,按award_level分组统计数量,前端用ECharts画一个饼图或柱状图。这不仅是功能上的加分项,还能展示你处理统计类SQL的能力。

第四,导出Excel。用EasyExcel或者POI把作家列表导出为Excel文件。虽然功能不难,但在“信息管理系统”里非常常见,也是企业里频繁使用的场景,是个性价比很高的功能点。

我个人的实际体会是,毕设项目从来不需要追求大而全,重要的是把一个技术链路走通、走顺、走明白。SpringBoot+Vue这套组合,你做完之后把每块细节都能讲清楚,答辩和面试都不会心虚。如果你也在做同类的管理系统项目,希望我这篇梳理能帮你把索引建起来、接口收口、文档补齐,该避的坑提前躲开。多花了三天时间打磨细节,答辩时呈现出来的气质完全不一样。

相关新闻

无硬件SPI也能读写EEPROM:25LC256模拟SPI驱动封装与移植

无硬件SPI也能读写EEPROM:25LC256模拟SPI驱动封装与移植

简介&#xff1a;这份资源是面向嵌入式开发初学者与电子工程师的EEPROM 25LC256读写源码包&#xff0c;采用C实现&#xff0c;重点演示通过模拟SPI方式完成对25LC256的完整读写控制。内容覆盖SPI接口初始化、片选与释放、命令与地址传输、读写使能、数据缓冲及错误处理等关键环…

2026/10/20 3:05:53 阅读更多 →
模拟SPI驱动25LC256 EEPROM读写源码解析与移植指南

模拟SPI驱动25LC256 EEPROM读写源码解析与移植指南

简介&#xff1a;这份资源是面向嵌入式开发初学者与电子工程师的EEPROM 25LC256读写源码包&#xff0c;采用C实现SPI模拟读写操作&#xff0c;帮助读者理解非易失性存储器的通信原理与驱动编写方法。源码围绕SPI接口初始化、片选控制、命令与地址传输、读写使能、事务管理及错误…

2026/10/20 3:05:53 阅读更多 →
Streamlit应用集合实战:架构拆解、模块复用与部署避坑指南

Streamlit应用集合实战:架构拆解、模块复用与部署避坑指南

简介&#xff1a;这是一套基于 Streamlit 框架制作的网站应用示例集合&#xff0c;面向 Python 数据科学家、工程师及数据分析师&#xff0c;帮助不具备前端开发背景的读者快速搭建数据可视化与交互式应用&#xff0c;降低 Web 界面开发门槛。该库强调“代码即文档”&#xff0…

2026/10/20 3:04:52 阅读更多 →

最新新闻

Playwright Fixture机制:模拟数据库与API,根治测试数据污染

Playwright Fixture机制:模拟数据库与API,根治测试数据污染

做Web自动化测试这几年&#xff0c;我在实际项目里总结出三怕&#xff1a;一怕测试环境数据库连不上&#xff0c;二怕第三方接口突然限流&#xff0c;三怕测试数据互相污染。前两个问题会让你辛辛苦苦维护的用例在一夜之间全部标红&#xff0c;第三个问题则更隐蔽——明明昨天还…

2026/10/21 7:59:12 阅读更多 →
Qt 5.15.12 Windows 10下编译32位动态库完整指南

Qt 5.15.12 Windows 10下编译32位动态库完整指南

简介&#xff1a;面向Windows 10 32位应用开发的Qt 5.15.12动态库编译包&#xff0c;基于MSVC2019编译器构建&#xff0c;专为需要维护32位兼容性、或希望避开复杂编译配置的C开发者准备。压缩包内共2000个文件&#xff0c;以Qt头文件&#xff08;.h&#xff09;为主&#xff0…

2026/10/21 7:59:12 阅读更多 →
机械臂正运动学入门:从旋转矩阵到DH参数解析

机械臂正运动学入门:从旋转矩阵到DH参数解析

机械臂这个东西&#xff0c;入门时最容易迷惑的不是电机怎么转&#xff0c;而是“我要它去的点”和“它实际到的点”之间到底隔着什么。我刚开始折腾机械臂时&#xff0c;以为把每个舵机角度调到某个值就行&#xff0c;结果想让它末端走一条直线&#xff0c;它硬是在空中画了个…

2026/10/21 7:59:12 阅读更多 →
Java设计模式速通:场景+实战,快速掌握核心模式

Java设计模式速通:场景+实战,快速掌握核心模式

不是每一行代码都值得背下来&#xff0c;但设计模式是少数值得花时间想清楚的东西。尤其是Java生态里&#xff0c;框架源码、中间件、业务系统到处都嵌着这些模式&#xff0c;看不懂它们&#xff0c;读源码就像看天书&#xff1b;看懂了&#xff0c;重构和扩展的底气完全不一样…

2026/10/21 7:59:12 阅读更多 →
zzcms8.3建站系统实战:环境搭建、代码审计与二次开发改造指南

zzcms8.3建站系统实战:环境搭建、代码审计与二次开发改造指南

简介&#xff1a;ZZCMS 8.3 是一套开源内容管理系统源码&#xff0c;面向希望入门代码审计与 PHP 架构的开发者、安全学习者及建站人员。它既可用于搭建动态网站&#xff0c;也是一份可动手剖析的实战样本&#xff0c;帮助读者在真实项目中理解安全编码与设计模式。压缩包为 zi…

2026/10/21 7:59:12 阅读更多 →
EVE新手挖坟逃生指南:反跳、扰频、微曲与泡泡全解析

EVE新手挖坟逃生指南:反跳、扰频、微曲与泡泡全解析

简介&#xff1a;这是一份面向EVE新手玩家的挖坟逃生实操指南&#xff0c;作者结合多年实战经验&#xff0c;系统梳理了遗迹破译与逃生环节最容易翻车的细节。内容从破译箱子时的隐身快捷键设置、环绕距离把握讲起&#xff0c;逐步展开过星门遇泡泡的脱身路线规划、微曲与隐身技…

2026/10/21 7:58:11 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →