尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
JavaWeb学生成绩管理系统:Servlet+JSP+JDBC三层架构实战解析
简介面向JavaWeb学习者与课程设计人群这是一份学生成绩管理系统完整项目基于Servlet、JSP、JSTL、EL等技术栈配合Eclipse与SQL Server实现成绩录入、查询、统计分析和报表生成适合毕业设计、课设或项目实践参考。压缩包共138个文件涵盖27个Java源文件与对应class字节码、23个SQL脚本、20个JSP页面以及Eclipse工程配置、PowerDesigner物理模型pdm/cdm和设计文档覆盖从建库到部署的完整链路整体大小仅3.87MB。已有12758人学习浏览。通过研读源码、SQL脚本与设计模型可掌握JavaWeb分层开发思路、数据库表结构设计及权限控制等关键实践配合文档还能快速理解系统架构便于二次开发与论文撰写。1. 期末成绩出来那周这个 JavaWeb 系统怎么把老师从 Excel 里捞出来某高校一位计算机老师期中考完拿到三个班 180 份纸质成绩单助教手动录 Excel合分、算及格率、按班级排名折腾了两天最后还是因为某列排序点错返工了一次。学生成绩管理系统这种 JavaWeb 项目解决的就是这类场景老师登录后录入成绩、按课程查询、按班级统计学生登录后只能看自己的成绩管理员维护学生和课程的基础数据。整个系统用 Servlet JSP JDBC MySQL 实现不依赖 Spring 全家桶Tomcat 一跑就能用。对正在做 JavaWeb 课设、或者想找一个分层清晰还能二次开发的成绩管理系统源码的人来说它是很好的起步项目没有复杂框架包裹三层结构一眼能看懂改起来很快。2. 整体架构与工程结构Servlet JSP JDBC 的三层拆分2.1 技术选型为什么不用 Spring 全家桶也能跑通这个系统没有引入 SSM 或者 Spring Boot而是用传统的 JavaWeb 技术栈Servlet 处理请求JSP 负责页面展示JDBC 直接操作数据库Maven 管理依赖Tomcat 作为运行时容器。放在现在的环境里看好像有点“原始”但恰恰是这种原始让它特别适合学习没有 AOP、IoC 那一层请求从浏览器到数据库的路径是直线断点打下去每一步都能跟到。常见做法是每张表对应一个实体类Servlet 调 ServiceService 调 DAODAO 里写 JDBC 代码。Service 层负责事务和业务校验DAO 层只做增删改查Servlet 里不出现 SQLJSP 里不出现 Java 脚本片段而是用 EL 和 JSTL 取值。这套规范和现在企业中 Spring MVC 的分层思路完全一致只不过容器换成了 Tomcat、注入换成了手动 new——理解了这套后面切到 Spring Boot 就是换壳而已。2.2 工程目录结构与各个包的分工先看目录结构项目是按标准 Maven Web 工程组织的src/main/java ├── com.demo.sms │ ├── entity # Student、Course、Score、User 实体类 │ ├── dao # 数据库访问层JDBC 操作 │ ├── service # 业务逻辑层事务、校验 │ ├── servlet # 控制器层处理请求跳转 │ ├── util # DBUtil 数据库连接工具 │ └── filter # 登录拦截、编码过滤器 src/main/webapp ├── login.jsp # 登录页 ├── admin # 管理员页面学生管理、课程管理 ├── student # 学生页面查看成绩 ├── teacher # 教师页面录入成绩、统计报表 └── WEB-INF/web.xml # 项目配置entity 是四张表对应的 Java 对象字段和表字段一一对应dao 包里每个实体一个 DAO 类比如 ScoreDao 里有 insert、findPage、count 等方法service 包是核心比如录入成绩时要先校验再插库这段逻辑放在 Service 里而不是 Servlet 里是为了复用——以后成绩批量导入、Excel 导入都要走同一个入口就不用在每个 Servlet 里重写一遍。filter 包里通常有两个过滤器一个处理全站编码一个做登录拦截静态资源放行未登录用户一律重定向到 login.jsp。这个分层有一点需要适应Servlet 和 Service 之间没有接口而是直接 new 实现类。小型系统没问题但如果你打算以后扩展成接口加实现类其实也很简单抽个接口然后把 new 改成工厂即可。我一般会先把 Service 层的方法签名固定住再决定要不要上接口。3. 数据库设计与成绩统计四张核心表和一段聚合 SQL3.1 表结构设计用户、学生、课程、成绩之间的关系成绩管理系统的数据库不复杂核心就四张表用户表、学生表、课程表、成绩表。用户表管登录学生表和课程表是基础数据成绩表是中间表承担学生和课程的多对多关系。CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE NOT NULL COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT MD5密文, role VARCHAR(16) NOT NULL COMMENT admin/teacher/student, ref_id INT COMMENT 关联学生ID或教师ID ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) UNIQUE NOT NULL COMMENT 学号, student_name VARCHAR(32) NOT NULL, class_name VARCHAR(64) NOT NULL COMMENT 班级如计算机2301, gender VARCHAR(4) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(64) NOT NULL, credit DECIMAL(3,1) DEFAULT 2.0 COMMENT 学分, teacher VARCHAR(32) COMMENT 任课教师 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2) NOT NULL COMMENT 成绩0-100, exam_type VARCHAR(16) DEFAULT 期末 COMMENT 平时/期中/期末, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_stu_course (student_no, course_id, exam_type) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;设计上要特别注意两点第一user 表里存的是 MD5 密文而不是明文登录时把用户输入的密码做同样加密再去比对防止数据库泄露后明文密码直接暴露第二score 表加了唯一键 (student_no, course_id, exam_type)这个唯一键是防重复录成绩的最后一道防线——即使代码里忘了判断数据库也会报错拦住你。这两个设计在课设项目里容易被忽略但实际答辩时老师盯着的往往就是这两处。成绩表通过 student_no 和 course_id 关联学生与课程join 时不需要再加冗余字段。如果查询量大了想优化可以给 student_no 建普通索引但学生成绩系统的数据量一般几千条非聚集索引足够用不用做分区。3.2 成绩统计的核心 SQL平均分、及格率一次查出来教师端最常用的功能是按课程统计成绩分布对应 SQL 是这段聚合查询SELECT c.course_name AS 课程名, COUNT(s.id) AS 总人数, ROUND(AVG(sc.score), 2) AS 平均分, MAX(sc.score) AS 最高分, MIN(sc.score) AS 最低分, ROUND(SUM(CASE WHEN sc.score 60 THEN 1 ELSE 0 END) / COUNT(s.id), 4) AS 及格率 FROM course c LEFT JOIN score sc ON c.id sc.course_id LEFT JOIN student s ON sc.student_no s.student_no GROUP BY c.id, c.course_name ORDER BY c.id;这段 SQL 里有三个关键点。LEFT JOIN 而不是 INNER JOIN选 LEFT 是为了让还没有任何人选课的课程也能出现在统计列表里总人数为 0及格率为 NULL页面上显示时再做兜底及格率用 CASE WHEN 把成绩转成 0 或 1 再求和避免了先查总人数再查及格人数的两次查询ROUND 到四位小数是及格率页面上乘 100 显示成百分点这些计算放 SQL 里比放 Java 代码里高效得多数据库本身是更擅长做聚合的。统计班级维度时把 GROUP BY 的字段换成 s.class_name 即可查询条件加一个 WHERE c.id ? 就可以看单门课程的数据。这段 SQL 在 MySQL 5.7 和 8.0 下都能跑但要注意 8.0 里如果开了 ONLY_FULL_GROUP_BYSELECT 出来的字段必须都在 GROUP BY 中出现或者被聚合函数包裹上面这段 SQL 已经规避了这个问题直接复制可用。4. 核心代码拆解从登录到成绩发布的完整链路4.1 登录鉴权把 Session 用起来登录是整个系统的入口。LoginServlet 接收用户名和密码调 UserDao 查询数据库比对成功把 User 对象放进 Session失败则转发回登录页并携带错误提示。代码按照 Servlet 3.0 的标准写法用 WebServlet 注解代替 web.xml 配置WebServlet(urlPatterns /login) public class LoginServlet extends HttpServlet { private UserDao userDao new UserDao(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password DigestUtils.md5DigestAsHex(req.getParameter(password).getBytes()); User user userDao.findByUsernameAndPassword(username, password); if (user null) { req.setAttribute(errorMsg, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); return; } req.getSession().setAttribute(loginUser, user); if (admin.equals(user.getRole())) { resp.sendRedirect(req.getContextPath() /admin/index.jsp); } else if (teacher.equals(user.getRole())) { resp.sendRedirect(req.getContextPath() /teacher/index.jsp); } else { resp.sendRedirect(req.getContextPath() /student/index.jsp); } } }这段代码有两个细节值得说明。密码在进入 DAO 之前先做了 MD5这样即使 SQL 查询日志被拿到里面也只有密文不会暴露原始密码比对失败时用的是 forward 转发而不是 redirect 重定向因为要把 errorMsg 放进 request 域里让登录页显示重定向会丢掉 request 属性。登录成功后走的是 redirect这个跳转方式虽然多了一次请求但能让浏览器地址栏变成真实的首页 URL用户刷新页面不会重复提交表单。至此 Session 中有了 loginUser后面的所有请求都可以通过它判断身份。配合 Filter 做拦截在 web.xml 或注解里配置过滤路径 /admin/、/teacher/、/student/*未登录用户访问这些路径直接重定向到 login.jsp。注意过滤链中要放行 login、css、js、images 这类资源否则用户连登录页样式都加载不出来。4.2 成绩录入Service 层的事务处理和校验成绩录入是教师端最核心的操作。一个教师可能同时录入一个班多门课的成绩所以录入接口设计成了批量模式。成绩录入不能只有“插入”这一个动作——还要判断学生是否存在、成绩是否合法、考试类型是否重复这些逻辑全部收敛到 ScoreService 中public void addScores(ListScoreParam params) { if (params null || params.isEmpty()) { throw new IllegalArgumentException(成绩数据不能为空); } for (ScoreParam p : params) { if (p.getScore() 0 || p.getScore() 100) { throw new IllegalArgumentException(成绩必须在0~100之间: p.getStudentNo()); } if (!studentDao.existsByNo(p.getStudentNo())) { throw new IllegalArgumentException(学号不存在: p.getStudentNo()); } } Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); for (ScoreParam p : params) { scoreDao.insert(conn, p); } conn.commit(); } catch (SQLException e) { try { if (conn ! null) { conn.rollback(); } } catch (SQLException ex) { log.error(回滚失败, ex); } throw new RuntimeException(成绩批量录入失败, e); } finally { DBUtil.close(conn); } }这段逻辑的重点在事务。默认情况下 JDBC 的 autocommit 是 true每条 insert 都是独立的如果第 10 条数据插入失败前面 9 条已经生效数据就变成半成品了——这就是典型的“录到一半库里多了半个班”。所以我会先 conn.setAutoCommit(false)全部插入成功再 commit任何一条异常都会触发 rollback保证一个批次的成绩要么全部入库要么全部不落。事务之外异常设计也值得说一下。Service 层抛 IllegalArgumentException 表示“用户的输入有问题”信息直接展示给教师抛 RuntimeException 表示“系统内部故障”信息记录在服务端日志页面只提示“录入失败请稍后重试”。这种方式在分层架构里叫异常翻译把底层异常统一包装成业务异常再向上抛比直接把 SQLException 甩给 JSP 页面更可控。唯一键 uk_stu_course 是兜底策略代码没拦住重复会由数据库再拦一次。4.3 分页查询PageBean limit 的经典搭法成绩列表不能一次性把所有数据查出来常见做法是 PageBean 封装当前页、每页条数、总记录数DAO 层用 LIMIT 做物理分页。先看一下 DAO 里的分页查询方法public ListScoreVO findPage(int offset, int pageSize, Integer courseId, String keyword) { StringBuilder sql new StringBuilder( SELECT s.student_no, s.student_name, s.class_name, c.course_name, sc.score FROM score sc JOIN student s ON sc.student_no s.student_no JOIN course c ON sc.course_id c.id WHERE 11 ); ListObject args new ArrayList(); if (courseId ! null) { sql.append(AND sc.course_id ? ); args.add(courseId); } if (keyword ! null !keyword.trim().isEmpty()) { sql.append(AND (s.student_name LIKE ? OR s.student_no LIKE ?) ); args.add(% keyword.trim() %); args.add(% keyword.trim() %); } sql.append(ORDER BY sc.create_time DESC LIMIT ?, ?); args.add(offset); args.add(pageSize); return query(sql.toString(), args); }参数 offset 是跳过的记录数pageSize 是每页条数两者都是从 PageBean 计算得到的。字符串拼接 SQL 时用 StringBuilder 和一个 list 累积参数条件用 IF 判断动态追加这样课程筛选和姓名模糊搜索可以自由组合WHERE 11 是为了避免拼接多个 AND 时写一堆判断。这里不能用字符串直接拼接参数值而应该用占位符 ? 再加参数否则用户输入的关键词如果带引号SQL 就会语法报错甚至被注入。Service 层计算 PageBean 时还要先查一次总记录数页面上才能展示“共 180 条 / 第 3 页”。常见实现是两个 DAO 方法一个是 count 查询一个查当前页数据Service 把两个结果组合public PageBean findPage(int pageNo, int pageSize, Integer courseId, String keyword) { int total scoreDao.countByCondition(courseId, keyword); PageBean pb new PageBean(pageNo, pageSize, total); ListScoreVO list scoreDao.findPage(pb.getOffset(), pageSize, courseId, keyword); pb.setList(list); return pb; }JDBC 拼接 LIMIT ? 时需要注意参数类型MySQL 的 LIMIT 参数要用 setObject 传整数不要拼成字符串否则部分驱动会因为类型转换报错。查询之后 JSP 端用 JSTL 遍历 PageBean 的 listc:forEach items${pageBean.list} varitem tr td${item.studentNo}/td td${item.studentName}/td td${item.className}/td td${item.courseName}/td td c:choose c:when test${item.score 60}${item.score}/c:when c:otherwisespan stylecolor:red${item.score}/span/c:otherwise /c:choose /td /tr /c:forEach这段 JSP 用了 EL 表达式取值用 c:choose 做条件判断低于 60 分的成绩标红。页面上方的分页条再放一个表单提交当前页码配合翻页按钮就能完成完整的分页交互。注意 EL 表达式里访问的是 JavaBean 的属性名也就是 getStudentNo() 对应的 studentNo写错大小写拿到的就是空值。5. 避坑记录部署和调试中的五个真实问题5.1 环境与部署类连不上数据库、跑错 Tomcat 版本第一条Tomcat 启动后访问登录页输入账号密码点击登录页面报 500后台堆栈显示 ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因是 mysql-connector-java 的 jar 包没有打进 WEB-INF/lib。用 Maven 时常见做法是在 pom.xml 中声明依赖并用 war 包方式打包如果你直接把项目作为目录部署到 Tomcat要检查 target 目录里有没有对应的 jar——没有就执行 mvn clean package 重新打包或者把 jar 手动复制到 WEB-INF/lib 下。这条坑在第一次部署时几乎必踩因为 Maven 编译成功并不代表依赖被复制到了运行目录。第二条Tomcat 版本选错。这个项目里的 Servlet 用的是 javax.servlet 包下的注解和 API对应的是 Tomcat 9。如果你电脑上装的是 Tomcat 10 或 11项目启动后所有页面能打开但一旦访问到 Servlet直接 404 或 500日志里提示 jakarta.servlet 包找不到。因为 Tomcat 10 开始把 javax.servlet 改名成了 jakarta.servlet包名变了旧项目不能在 Tomcat 10 上直接跑。解决方法是换回 Tomcat 9或者把所有 import 从 javax.servlet 全部替换为 jakarta.servlet明显前者省事。第三条项目明明能编译但部署后点任何按钮都跳到 404。原因通常是 Servlet 的 WebServlet 注解路径和表单的 action 路径不一致比如注解写的是 /scoreList表单却提交到了 /score/list。这类问题排查时先看浏览器地址栏的实际 URL再在 IDEA 里按住 Ctrl 点击跳转到对应的 Servlet 类路径一对就知道问题出在哪。我一般会定一个约定所有 Servlet 都用注解配置路径后台页面统一通过相对路径跳转避免出现多层目录下的路径混乱。5.2 编码与数据类中文乱码、重复提交第四条页面上中文都正常但是录进去的成绩在学生端查出来变成问号。这是典型的字符集问题最常见的原因是 JDBC 连接串里少了编码参数。很多新手写连接如下jdbc:mysql://localhost:3306/sms这串是没有指定 characterEncoding 的。完整写法是 jdbc:mysql://localhost:3306/sms?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai加 serverTimezone 是因为 MySQL 8.0 默认时区与驱动不一致会报错。除了连接串还要检查三处数据库表默认字符集是否为 utf8mb4、JSP 页面是否设置了 pageEncoding 与 contentType、Servlet 里是否调用了 req.setCharacterEncoding(UTF-8)。三处全部统一乱码问题才能根治。第五条教师点击“保存成绩”后刷新页面发现同一批成绩录入了两遍。原因是保存成功用了 forward 转发到列表页刷新时浏览器把上一个 POST 请求重新提交了一次。解决这个问题专门有一个模式叫 Post-Redirect-GetServlet 处理完 POST 请求后不要 forward而是 redirect 到查询页面浏览器地址栏变成 GET 请求刷新就只会重新拉列表而不会重放插入操作。这段逻辑在 4.1 的登录跳转那里已经体现但很多人只在登录用了重定向业务保存时又习惯性转发这是血泪经验。再配合 score 表上的唯一键兜底双保险才不会翻车。6. 二次开发指南给系统加一个成绩导出功能6.1 改造点导出 CSV 的 Service 与 Servlet 实现这个系统学生和教师端都有成绩列表但缺一个“导出 Excel”的能力。给它加一个导出功能是个很典型的二次开发场景涉及 Service 层新方法、Servlet 新路由、前端新增按钮三个改动。如果不想一下子引入 Apache POI 那么重的依赖可以先做 CSV 导出——Excel 能直接打开 CSV 文件实现成本低代码量只有二十行左右public void exportCsv(OutputStream out, Integer courseId) { ListScoreVO list scoreDao.findPage(0, 10000, courseId, null); StringBuilder sb new StringBuilder(); sb.append(\uFEFF).append(学号,姓名,班级,课程,成绩,是否及格\n); for (ScoreVO vo : list) { sb.append(vo.getStudentNo()).append(,) .append(vo.getStudentName()).append(,) .append(vo.getClassName()).append(,) .append(vo.getCourseName()).append(,) .append(vo.getScore()).append(,) .append(vo.getScore() 60 ? 及格 : 挂科) .append(\n); } out.write(sb.toString().getBytes(StandardCharsets.UTF_8)); }这段代码第一个字符 \uFEFF 是 UTF-8 的 BOM 头没有它 Excel 打开 CSV 文件时中文列名会乱码。经验是写导出功能时一定要先处理 BOM否则导出给老师看第一行就全是问号体验直接崩塌。Servlet 端把输出流指向 resp.getOutputStream()并设置 Content-Type 为 text/csv文件名用 URLEncoder 处理一下再拼到 Content-Disposition 里浏览器就会自动下载而不是在页面里打开。6.2 验证方法前后端两处检查改完代码后的验证顺序先用前端的“导出”按钮触发请求看浏览器是否弹出文件下载下载后打开文件第一行列名正常、中文不乱码、成绩数字为两位小数再把 courseId 参数传一个不存在的值确认后端返回空文件而不是 500。如果以后要导出真正的 .xlsx 格式无非是把字符串拼接换成 new XSSFWorkbook() 然后逐行 setCellValue导出逻辑留在 Service 层的好处在这里就体现了——Service 返回统一的 List 换成任何导出方式都不用动 Servlet 和页面。我曾经在这个系统上踩过最大的坑就是导出 CSV 时没带 BOM自己本地用记事本看一切正常发给老师后全部乱码。从那以后我每写一个导出功能都强制检查文件流的编码头设置导出完先自己下下来双击打开确认无误再交付。希望这篇解析能让你少走这段弯路——把一个基础的学生成绩管理系统跑起来不难把分层的思路看明白后面任何 JavaWeb 项目都能很快上手。本文还有配套的精品资源点击获取
RELATED

相关推荐

MCreator 基岩版方块模型完全指南:Normal / Cross / 单纹理 / 自定义 `.geo.json` 模型的选择与渲染原理

MCreator 基岩版方块模型完全指南:Normal / Cross / 单纹理 / 自定义 `.geo.json` 模型的选择与渲染原理

低代码代码生成桌面应用游戏开发 【免费下载链接】MCreator MCreator is an open-source software used to make Minecraft Java Edition mods, Minecraft Bedrock Edition Add-Ons, resource packs, and data packs using an intuitive easy-to-learn interface or with an in…

📅 2026/10/16 6:47:07
instagram-private-api 内联子评论响应结构详解:MediaInlineChildCommentsFeedResponseChildCommentsItem 接口深度剖析

instagram-private-api 内联子评论响应结构详解:MediaInlineChildCommentsFeedResponseChildCommentsItem 接口深度剖析

后端 【免费下载链接】instagram-private-api NodeJS Instagram private API SDK. Written in TypeScript. 项目地址: https://gitcode.com/gh_mirrors/in/instagram-private-api 点击查看 免费下载 本文围绕 instagram-private-api(TypeScript 编写的 …

📅 2026/10/16 6:47:07
Calibre 电子书格式转换:3 次点击,让书进库就能发到任何阅读器

Calibre 电子书格式转换:3 次点击,让书进库就能发到任何阅读器

Calibre 电子书格式转换:3 次点击,让书进库就能发到任何阅读器 【免费下载链接】calibre The official source code repository for the calibre ebook manager 项目地址: https://gitcode.com/GitHub_Trending/ca/calibre Calibre 是一款免费开源…

📅 2026/10/16 6:47:07
MORE NEWS

更多资讯

📰

程序员转型AI大模型:机遇、陷阱与实战路线全解析

我自己在工程师圈子里观察到一个现象:最近半年,讨论“程序员转型AI大模型”的人,比讨论框架版本更新的人还多。打开技术社区,永远有人在问“现在转大模型还来得及吗”;朋友圈里,不是晒大模型课程打卡&#…

📰

Git新手5步实操地图:从clone到push的最小闭环

1. 这不是命令行说明书,而是一张你随时能抄作业的Git操作地图“10分钟图文并茂手把手教会你Git常用指令”——看到这个标题,我第一反应不是点开,而是把手机倒扣在桌面上,泡了杯浓茶。为什么?因为过去三年里&#xff0c…

📰

面向过程与面向对象:本质是建模思维的分岔路口

1. 为什么“面向过程”和“面向对象”不是两种写法,而是两种思考方式?刚入行那会儿,我带过几个零基础转行的学员。有位A同学学完C语言函数调用后信心满满,说:“面向过程不就是把任务拆成一个个函数嘛?”接着…

📰

Git高频指令速查手册:10个救命命令手把手教学

1. 这不是Git教程,是“能立刻用起来”的指令速查手册你有没有过这种时刻:刚在团队群里被,说“主干分支被误提交了,快回滚”,你手指悬在键盘上,脑子里却只浮现出git push和git pull两个词?或者写…

📰

给AI装上“心智”:陪伴场景下的五模块状态框架

我一直觉得,聊天陪伴型AI最难的地方不在“会说话”,而在“有状态”。你把市面上一款主流对话模型接上记忆库、加好人设提示词,刚开始聊体验确实不错;可聊上一周,那种“空洞感”很快就浮出来了——同一个问题&#xff0…

📰

OpenCV Python人脸识别实战:从环境搭建到实时检测与识别

1. 从零搭建人脸识别环境:版本选择和安装陷阱先说明一下,这篇文章是一篇我自己从头做过很多遍的实战记录,不是什么理论科普。标题里提到的OpenCV和Python人脸识别,是我在实际项目里用了很久的一条技术路线。它最大的优势就是轻量、…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬