news 2026/9/26 4:21:18

学生成绩管理系统实战:Spring Boot+MySQL核心设计与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
学生成绩管理系统实战:Spring Boot+MySQL核心设计与避坑指南

简介:这份资料包围绕“学生成绩管理系统的设计与实现”提供论文与完整源码,适合高校计算机相关专业学生用于毕业设计、课程设计,也可作为Web开发初学者的对照学习资料。压缩包共544个文件,大小20.14MB,主体包含ASP/ASPX网页文件、C#源代码、数据库文件(.mdf/.ldf)、Word/PDF文档,以及大量jpg/png/gif图片素材,可覆盖前端页面、后端逻辑、数据库设计与论文撰写等环节。目前已有97人学习下载,内容结构完整,除源码与论文外还配有数据库脚本、配置文件和工程文件,便于在Visual Studio中研究。借助这一系统可掌握学生信息管理、成绩录入、查询统计、报表生成等核心模块的设计思路,并理解从需求分析到系统部署的完整流程,对完成课程设计或毕业答辩具有实际参考价值。

1. 学生成绩管理系统到底在做一个什么事

先抛一个反直觉的结论:这种课程设计里,代码中的成绩计算写得越绕,答辩被追问的概率越高。学生成绩管理系统的核心需求就三件事——成绩录入、成绩查询、成绩统计,外加一个角色登录。真正让你翻车的不是某个排序算法,而是数据库约束、事务边界、中文字符集这些平时不显眼的细节。

这个课题适合两类人:一类是正在选 Java 课程设计题目、想找一个能完整覆盖增删改查和统计报表方向的在校生;另一类是工作后想快速复现一个经典 CRUD 项目、借此补一补 Spring Boot 与 MySQL 基本功的开发者。把「论文+源码」当黑匣子直接解压交差没有意义,关键是知道内部如何组织、答辩会从哪个角度追问,以及哪些地方一改就崩。

2. 先把需求拆成表:学生、课程、成绩与权限的数据模型怎么定

课程设计里最容易出现的毛病,是一上来就建十几张表,仿佛表越多越专业。实际上学生成绩管理系统就是围绕「学籍、课程、成绩、账号」四个业务对象转。把关系理清楚,代码层就省掉大半麻烦。

2.1 三张核心表:字段类型、主键策略与唯一约束

先看业务需求清单。教师需要录入成绩、按班级查看名单;学生需要按学期查看自己的成绩和排名;管理员要维护课程和账号。三张表足够承接:student 存学籍,course 存课程,score 存成绩。下面是平时我会直接用的建表脚本,注意注释部分就是踩坑点。

CREATE TABLE student ( id INT NOT NULL AUTO_INCREMENT COMMENT '物理主键', student_no VARCHAR(20) NOT NULL COMMENT '学号', name VARCHAR(50) NOT NULL COMMENT '姓名', gender CHAR(6) DEFAULT '男' COMMENT '性别', class_name VARCHAR(50) COMMENT '班级', enroll_year VARCHAR(4) COMMENT '入学年份', PRIMARY KEY (id), UNIQUE KEY uk_student_no (student_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表'; CREATE TABLE course ( id INT NOT NULL AUTO_INCREMENT, course_no VARCHAR(20) NOT NULL, course_name VARCHAR(100) NOT NULL, credit DECIMAL(3,1) DEFAULT 0 COMMENT '学分,可为0.5', teacher_name VARCHAR(50) COMMENT '任课教师', PRIMARY KEY (id), UNIQUE KEY uk_course_no (course_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程表'; CREATE TABLE score ( id INT NOT NULL AUTO_INCREMENT, student_id INT NOT NULL COMMENT '关联student.id', course_id INT NOT NULL COMMENT '关联course.id', semester VARCHAR(20) NOT NULL COMMENT '学期,如 2024-2025-1', score DECIMAL(5,2) NOT NULL COMMENT '成绩,保留两位小数', created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_student_course_semester (student_id, course_id, semester), KEY idx_course_id (course_id), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student (id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成绩表';

为什么 student 表不直接用学号做主键?学号是业务字段,学校一旦调整学号编码规则或者发生合班,主键就要跟着改,外键全部级联,代价很大。用自增 id 做主键,学号只做唯一索引,业务变更与物理主键分离,这是数据库设计里最基础也最容易被忽略的一课。

score 表里唯一键(student_id, course_id, semester)的作用是防重复录入:一个学生同一学期同一门课只能有一条成绩。这个约束写在数据库层,比在 Java 代码里if (exists)可靠得多,因为并发请求可能会同时通过代码检查。外键在这里保留了完整性约束,但生产环境大流量下会优先拆掉外键换应用层保证——课程设计阶段留着外键反而能给答辩加分,因为评审老师想看你懂。

另一个细节是DECIMAL(5,2)。成绩如果允许 59.5、补考 60.5 之类的小数,用DECIMAL,不要用FLOAT。FLOAT是近似存储,算平均分时会出现 78.4500001 这种奇怪尾巴,到时候不好解释。

2.2 统计视图:平均分、及格率用一条 SQL 說清楚

答辩大概率会问「成绩统计是怎么实现的」,如果答「把数据查出来在 Java 里 for 循环算」,印象分会掉一截。常见做法是直接在数据库聚合,因为数据库的聚合函数经过大量优化,代码量也少。

SELECT c.course_no, c.course_name, ROUND(AVG(s.score), 2) AS avg_score, COUNT(*) AS total_count, ROUND(SUM(CASE WHEN s.score >= 60 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS pass_rate FROM score s JOIN course c ON s.course_id = c.id GROUP BY c.course_no, c.course_name ORDER BY c.course_no;

这段 SQL 有两点值得在论文里写清楚:第一,AVG会自动忽略 NULL,如果成绩表里有缺考记录(score 为 NULL),平均分的分母就不包含缺考人数,如果需求要求缺考算 0 分,必须先IFNULL(score, 0)再算;第二,及格率里用SUM(CASE WHEN ...)而不是COUNT(IF(...)),两种写法在这个场景下都能跑通,但CASE WHEN的可读性更好,也方便扩展成「90 分以上优秀率」这类衍生指标。

用 GROUP BY 统计时,ORDER BY后面最好跟上分组字段course_no,不要按聚合函数AVG排序——课程号排序结果稳定,考试排名式展示反而让数据在页面里反复跳动。

2.3 登录账号与角色:一张用户表还是四张 RBAC 表

学生成绩系统的登录一般分三种角色:管理员、教师、学生。很多同学会直接往 student 表里塞password字段,这个设计在答辩现场基本会被一票否决——密码属于账号信息,不属于学籍信息,二者生命周期不同。学生休学退学后学籍可能被归档,但账号往往需要保留。

实际项目里我更常用一张用户表加一个角色字段的方案,三表模型能省掉角色关联表的 JOIN:

CREATE TABLE sys_user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(30) NOT NULL, password VARCHAR(100) NOT NULL COMMENT 'BCrypt哈希后的值', role TINYINT NOT NULL DEFAULT 2 COMMENT '1管理员 2教师 3学生', student_id INT NULL COMMENT '学生角色关联student.id', teacher_name VARCHAR(50) NULL COMMENT '教师角色保存姓名', PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账号表';

这个设计的取舍点在于:如果论文要求写「基于角色的权限管理」,单表角色字段太单薄;但 90% 的课设场景下,用户量只有几十个,权限也只有按钮级,四张 RBAC 表反而让登录逻辑模板化、冗长。折中做法是把角色字段放 sys_user,权限判断用拦截器按 role 值分发,论文里写清「本系统采用轻量级角色模型,若后续扩展为动态权限可平滑迁移到 RBAC」,既是真心话,也能挡住评审追问。

3. 后端接口与服务层:用 Spring Boot 把一个 CRUD 写成能答辩的工程

数据模型定了,接下来就是后端代码。这里说的不是贴一整份工程源码,而是把「分层、登录、录成绩」三个关键链路拆开看,参数怎么传、事务放哪层、异常怎么抛。

3.1 包结构与分层:Controller 只做转发,Service 只做业务

常见做法是 Spring Boot + MyBatis,包结构如下:

com.sms ├── controller # HTTP 入口,只做参数接收和结果封装 ├── service # 业务层,写规则和事务 │ └── impl ├── mapper # MyBatis 的数据访问接口 ├── entity # 数据库表对应的实体 ├── common # 统一返回 Result、异常处理 └── config # 拦截器、跨域等配置

这种分层最核心的一句话:Controller 不要写 if。很多课设源码里能见到「把查重、计算平均分、判断权限全写进 Controller」的写法,导致一个方法几百行。更好的方式是 Controller 里只保留参数绑定、调用 Service、返回 Result,业务规则全部下沉。

只贴一个最小 Controller 示例:

@RestController @RequestMapping("/api/score") public class ScoreController { @Autowired private ScoreService scoreService; @PostMapping("/add") public Result add(@RequestBody ScoreDTO dto) { // 第1步:参数合法性校验 if (dto.getScore() < 0 || dto.getScore() > 100) { return Result.fail("成绩必须在0到100之间"); } // 第2步:调用Service业务方法,返回业务结果码 int rows = scoreService.addScore(dto); return rows > 0 ? Result.ok() : Result.fail("该学生本课程成绩已存在,不能重复录入"); } }

Result是统一返回对象,一般有三个字段:code、message、data。课程设计里 code 用 200 表示成功、500 表示业务失败就够用了,不必照搬公司里那套错误码规范。这个类写在 common 包,所有接口统一返回它,前端判断逻辑就只需要关注一个结构。

3.2 登录与拦截器:密码不能明文存

登录接口是几乎所有课程设计里第一个被评审老师翻开看的代码。如果源码里把用户密码直接明文存数据库,基本上等于告诉老师「我没学过安全设计」。这里用 Spring Boot 集成的常见方案:BCrypt 加密。

@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private PasswordEncoder passwordEncoder; @Override public User login(String username, String rawPassword) { User user = userMapper.selectByUsername(username); // BCrypt校验:密码正确返回用户,否则返回null if (user != null && passwordEncoder.matches(rawPassword, user.getPassword())) { return user; } return null; } }

注意登录逻辑里有三个细节:第一,passwordEncoder.matches接收的是前端传来的明文和数据库里已加密的密文,比较过程由 BCrypt 内部完成,不需要自己解密;第二,查询按用户名命中后,如果用户不存在,也要返回同样的提示「用户名或密码错误」,避免通过登录接口探测用户名是否存在;第三,登录成功后的会话凭证存放在 HttpSession,拦截器根据 Session 里有没有loginUser来判断是否放行。

实际工程里这个题目也经常改用 JWT,把 token 放在请求头里。课程设计选 Session 方案最省事,因为不需要额外处理 token 过期和刷新逻辑,论文里还能写「基于 Session 实现会话管理」一笔带过。

3.3 成绩录入事务:重复判断与写入必须在一个事务里

成绩录入是系统里最核心的写操作,隐藏的问题也最多。先看 Service 实现:

@Service public class ScoreServiceImpl implements ScoreService { @Autowired private ScoreMapper scoreMapper; @Override @Transactional(rollbackFor = Exception.class) public int addScore(ScoreDTO dto) { // 事务内先查重 Score query = new Score(); query.setStudentId(dto.getStudentId()); query.setCourseId(dto.getCourseId()); query.setSemester(dto.getSemester()); Score exists = scoreMapper.selectOne(query); if (exists != null) { return 0; // 已存在,不写入 } // 构造实体并插入 Score score = new Score(); score.setStudentId(dto.getStudentId()); score.setCourseId(dto.getCourseId()); score.setSemester(dto.getSemester()); score.setScore(dto.getScore()); return scoreMapper.insert(score); } }

这段代码的边界点在于@Transactional的放置位置。注解必须加在public方法上,而且要通过 Service 对象调用才生效——Controller 里直接注入 Mapper 绕过 Service,事务就失效了。rollbackFor = Exception.class这个参数也不能省,Spring 默认只在 RuntimeException 时回滚,如果 MyBatis 抛 SQLException 以外的受检异常,事务可能提交成一半。

另外一个课程设计里容易被忽略的点:如果数据库在 score 表上建了唯一键,这里的selectOne查重其实只是「提前发现」,真正防住重复的是数据库唯一约束。这两层机制一个兜底一个提示,在论文里写设计思路时可以强调这是「乐观检查 + 数据库约束」的双保险,比只做代码判断更能体现工程意识。

3.4 成绩统计接口:把 SQL 放在 Mapper 里而不是 Service 里拼接

统计逻辑虽然推荐用 SQL 写,但放到哪里也很关键。常见做法是在 Mapper 接口定义一个方法:

@Mapper public interface ScoreMapper { List<Map<String, Object>> selectCourseStats(); }

对应的 XML 或注解 SQL 就是第 2 章那段 GROUP BY 查询。Mapper 方法返回List<Map<String, Object>>,Service 拿到后直接包装进 Result 返回前端。这样做的好处是结构清晰:SQL 变动只改 Mapper,不需要动 Service 代码。有人会把这段 SQL 用字符串拼接在 Service 里,这是 MyBatis 使用中最常见的坏味道——SQL 一旦写成字符串,续行、转义、参数注入的风险都会放大。

4. 前端页面与联调:三个页面把系统撑起来

学生成绩管理系统的前端课设版本大致有三种形态:JSP 服务端渲染、Thymeleaf 模板、纯 HTML + fetch 调用后端 API。多数带源码的课设包是 JSP 或 Thymeleaf 混着写,但实际演示效果最稳的是静态页面 + API 分离。

4.1 页面组织:列表页、表单页、统计页的最小集

页面不需要多,三个就够撑住完整业务流程:

  • 成绩列表页:带学号或姓名的模糊查询,支持按学期筛选;
  • 成绩录入/编辑页:弹窗或独立表单,录入学生、课程、分数;
  • 统计页面:用表格展示每个班级或课程的平均分、及格率。

用一张表列出各页面对应的文件和后端接口,会让论文结构与源码对得上号:

页面组件对应文件后端接口
登录页login.htmlPOST /api/user/login
成绩列表score_list.htmlGET /api/score/list
录入弹窗score_form.htmlPOST /api/score/add
统计视图stats.htmlGET /api/score/stats

页面之间通过侧边栏或顶部 Tab 跳转,只保留一个公共导航,不搞菜单嵌套。这类课设项目的前端体积不该做大,页面越少,联调越稳,答辩现场翻车的概率就越低。

4.2 列表查询的请求与渲染:fetch 的最小可用写法

如果后端是纯 JSON 接口,前端用 fetch 就够了,不需要引入 axios。成绩列表页的核心逻辑如下:

async function loadScoreList() { // 从表单读取筛选条件 const params = new URLSearchParams(); params.append('semester', document.getElementById('semester').value); // 发起请求 const resp = await fetch(`/api/score/list?${params.toString()}`, { credentials: 'include' // 带上Session Cookie }); const json = await resp.json(); if (json.code !== 200) { alert(json.message); return; } renderTable(json.data); }

这里有两个参数说明。credentials: 'include'是必须的:后端用 Session 方案时,前端如果不主动携带 Cookie,登录状态就传不过去,接口会一直 401。第二个是params.toString()会把值做 URL 编码,不要手拼'?semester=' + semester,遇到中文或特殊符号会出幺蛾子。

renderTable是表格渲染函数,课设里直接用innerHTML拼接字符串就能完成,不一定要引入 Vue 或 React。如果读者想显得工程化,也可以直接下载 Vue 3 的 CDN 版本用,但课程设计阶段页面简单,框架层不是评分重点。

4.3 状态码与数据约定:前后端联调的三个规矩

后端返回格式统一为{ code, message, data },前端就只需要根据code分支处理。最容易踩的坑是后端把业务失败抛成了 HTTP 500,前端fetch会走catch分支,popup 弹的是「网络错误」而不是「成绩已存在」。

我在联调时会定三条规矩:
第一,HTTP 状态码只表达传输层结果,200 表示请求到了后端,业务成功与否看code;
第二,后端所有校验失败都返回Result.fail(message),Controller 不抛异常;
第三,前端所有resp.ok只做网络层判断,业务判断一律看json.code。

这三条坚持下来,前后端各写各的也能对得整齐。很多课设源码里前后端状态码约定混乱,一会儿用 HTTP 404 表示「未找到学生」,一会儿又用code: -1,最后前端一堆 if 分支互相矛盾,改起来头皮发麻。

5. 部署与验收的 5 个常见坑:现象、原因、解决

这章写的都是平时带课设项目最常遇到的血泪问题。每一条都按「现象 → 原因 → 解决」来,方便直接对号入座。

5.1 页面中文全部是问号

现象:启动系统打开页面,学生姓名、课程名全部显示成?或者乱码。

原因:编码问题出在三个环节,通常是至少一个环节不一致。数据库表用了utf8mb4,但连接串没指定characterEncoding;或者前端页面本身是GBK编码;再或者 MySQL 服务端初始化时没设置字符集。

解决:逐层排查。先确认页面<meta charset="utf-8">;再确认 Spring Boot 的application.properties里连接串带了?useUnicode=true&characterEncoding=utf8;最后确认表本身SHOW CREATE TABLE score的 CHARSET 是 utf8mb4。三层一致后重启服务,问题基本消失。

5.2 MySQL 8 连接失败:Public Key Retrieval is not allowed

现象:本地 MySQL 8 环境启动项目,报错Public Key Retrieval is not allowed。

原因:MySQL 8 默认使用 caching_sha2_password 认证,客户端第一次连接时需要从服务端获取公钥加密密码,而 JDBC 默认禁止自动获取公钥。这个报错在毕业设计环境里极其常见。

解决:在 JDBC 连接串上追加两个参数:

spring.datasource.url=jdbc:mysql://localhost:3306/sms?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai

allowPublicKeyRetrieval=true允许客户端获取公钥,serverTimezone=Asia/Shanghai顺手解决时区偏差问题。这两个参数不属于高危配置,课程设计环境使用没问题,但生产环境通常会设置 SSL 证书而不是直接关掉。

5.3 打包成 JAR 后页面和接口都 404

现象:开发时mvn spring-boot:run一切正常,打成 JAR 用java -jar运行后,静态资源或页面全部 404。

原因:Spring Boot 内嵌 Tomcat 对静态资源的根路径敏感。如果前端页面放在src/main/webapp下,而项目用的是spring-boot-starter-web打 JAR 包,webapp 目录默认不会被包含。Spring Boot 官方推荐把静态资源放src/main/resources/static。

解决:把 HTML、CSS、JS 全部移到src/main/resources/static目录,然后通过http://localhost:8080/xxx.html访问。如果项目结构已经是 webapp,则改用 WAR 包部署,但课设用 JAR 更省事,所以迁移目录是最快解法。

5.4 重复录成绩时数据库报 duplicate key

现象:快速双击提交按钮两次,界面提示数据库异常Duplicate entry,而不是友好的「记录已存在」。

原因:前端双击导致请求发两次,后端两个线程几乎同时通过selectOne检查,都认为没有记录,然后一起执行 insert,唯一键生效但异常没被捕获。

解决:代码层捕获唯一键冲突并翻译成业务提示是最终防线。具体做法是在 Service 中对插入操作做异常兜底:

@Override @Transactional(rollbackFor = Exception.class) public int addScore(ScoreDTO dto) { try { return scoreMapper.insert(score); } catch (DuplicateKeyException e) { return 0; // 由Controller统一返回重复提示 } }

同时前端在提交按钮点击后禁用按钮,一个成熟的小交互能避免一大半重复请求。两层都做,数据才是稳的。

5.5 论文里的表结构与源码数据库对不上

现象:答辩前检查代码,发现论文里的 E-R 图有 6 张表,源码里实际只建了 4 张;论文写的字段是student_name,代码里是name。评审现场被问到细节,当场卡壳。

原因:多数课设包是论文和代码分开拼凑起来的,手里的源码可能被改过三轮,论文没有同步更新。

解决:拿到这类项目包后做三件事:第一,按论文的数据字典核对数据库所有表名与字段名,不一致的以当前代码为准,回改论文;第二,把数据库初始化脚本(通常叫init.sql或schema.sql)在本地清空重建一次,确认论文里提到底初始数据量能真实跑出来;第三,论文中的「系统功能模块图」要逐个对应页面菜单,保证每个入口都能点到。这一步不花代码功夫,但答辩的「一致性」印象分全靠它。

6. 从「完成」到「可答辩」:用三十分钟把项目讲成一个闭环

代码跑通只是第一步,答辩更像是一场「设计意图」的陈述。准备时间不用太长,按下面这个顺序走一遍,能把容易被问倒的点提前堵上。

6.1 准备一段「设计取舍说明」

老师问「为什么用户表不拆角色表」,不要回答「别人这么写的」。准备一段两句话的取舍理由:当前系统用户量小、角色只有三种,单表角色字段查询简单;如果后续接入年级、班级的多层权限,可以迁移为 RBAC 四表模型。同理,「为什么用存储过程而不用 Java 算」这类问题也提前想好,一句话答完,不要展开长篇。

6.2 演示顺序按「登录 → 录成绩 → 查统计 → 验证防重」来

进门先登录,然后录入一条成绩,再到列表确认数据出现,接着打开统计页看平均分变化,最后故意再提交一次同一条成绩,弹出「已存在」提示。这 30 秒展示的是完整闭环,比展示十几个页面更能留下「系统可靠」的印象。

6.3 最后一个习惯

帮人调完这类系统,我最后一定会删库重建一次,用初始化脚本从头跑一遍。这个过程虽然花几分钟,却能暴露出「别人机器上能跑、你机器上跑不了」的所有环境玄学。数据库连接串、初始密码、基础数据缺一不可,确认过了才敢说这套题能交。这几年带过不下十个学生重构这套成绩管理系统,每次翻车都翻在重复录入与编码这类小细节上,而不是什么高深算法。把这两处管住,这个题目就算真正拿下了。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 4:21:17

SpeedTree 1.6.0与SpeedTreeRT:老植被渲染工具链集成全攻略

简介&#xff1a;这是一份 SpeedTreeRT 1.6.0 源码及 CMake 构建解析学习包&#xff0c;面向游戏开发、影视特效及虚拟现实领域的 C 开发者&#xff0c;帮助理解专业级树木渲染引擎的工程实现与编译流程。包内共 59 个文件&#xff0c;以头文件与源码为主&#xff08;30 个 h、…

作者头像 李华
网站建设 2026/9/26 4:20:15

亲测有效!上海健康环保整木定制工厂案例分享

开篇&#xff1a;定下基调随着人们对居住环境品质要求的不断提升&#xff0c;健康环保整木定制成为家居装修的新趋势。本次测评旨在帮助消费者挑选出真正值得信赖的健康环保整木定制工厂。参与测评的产品来自汉斯&#xff08;上海&#xff09;智能家居科技股份有限公司&#xf…

作者头像 李华
网站建设 2026/9/26 4:20:14

企业微信代开发回调验签与AES解密实战指南

简介&#xff1a;这是一份面向Java开发者的企业微信代开发应用回调处理核心代码包&#xff0c;专为快速集成企微代开发回调能力而设计&#xff0c;解决开发者在签名验证、XML解析、GET校验与POST异步响应等环节重复造轮子的痛点。资源共46个文件&#xff0c;包含31个Java源码&a…

作者头像 李华
网站建设 2026/9/26 4:18:51

鸿蒙适配实践:jose_plus 与 JOSE 体系高性能安全令牌治理

1. 项目背景&#xff1a;为什么要在鸿蒙上做 JOSE 治理说实话&#xff0c;第一次看到 jose_plus 这个组件要适配鸿蒙的需求时&#xff0c;我心里是打了个问号的。移动端搞安全令牌&#xff0c;大家第一反应都是 JWT&#xff0c;而 Flutter 生态里 JWT 相关的库一抓一大把&#…

作者头像 李华
网站建设 2026/9/26 4:17:55

注释即系统宪法:黄金三角注释驱动工程可维护性

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

作者头像 李华
网站建设 2026/9/26 4:17:54

Feed 缓存不要缓存用户态:用页骨架和条目片段拆开共享数据

用户 A 点赞后立即刷新首页&#xff0c;用户 B 同时打开同一页&#xff1a;标题、封面和作者可以共用缓存&#xff0c;但两个人看到的 liked 必须不同。公开 Feed 的缓存核心不是多堆一层数据&#xff0c;而是把可共享的公共内容与按用户变化的状态拆开&#xff1a;Caffeine 抗…

作者头像 李华