news 2026/9/24 19:44:32

JavaWeb超市会员管理系统:JSP+Servlet+JDBC毕设实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaWeb超市会员管理系统:JSP+Servlet+JDBC毕设实战解析

简介:这是一套基于Javaweb的超市会员管理系统毕业设计项目,面向计算机相关专业正在做毕设的学生,以及需要项目实战练习的Java学习者,也可作为课程设计或期末大作业使用。系统采用JSP、Servlet、JDBC配合MySQL数据库,开发环境为JDK、Eclipse和Tomcat,功能涵盖会员管理、超市运营等核心模块,界面简洁、操作便捷。压缩包共742个文件、约15.72MB,以jpg/png图片、java源码、js脚本、css样式、html页面和sql数据库脚本为主,辅以xml配置与项目说明,目录清晰,方便导入开发工具直接运行。资源经严格调试确保可运行,附带数据库脚本、软件工具与项目使用说明,帮助快速搭建环境并理解完整实现逻辑。目前已有668人学习下载,适合需要完整毕业设计参考方案或实战练习的读者。

1. 这套Javaweb超市会员管理系统:毕设能跑、课设能交、练习能上手

做毕设最怕的不是题目难,而是拿到一套号称能运行的源码,导入Eclipse后满屏红叉,Tomcat一启动就报ClassNotFound。这套基于Javaweb的超市会员管理系统,是我近期拆过的最省心的一套JSP+Servlet+JDBC老牌技术栈项目。后台没有花哨的Spring全家桶,就是最经典的JSP页面、Servlet控制层、JDBC操作MySQL,界面用Bootstrap框架搭的,功能覆盖会员管理、商品管理、积分兑换这些超市核心业务场景。对计算机专业正在做毕业设计的学生来说,它可以直接当毕设交;对想练Javaweb基本功的初学者来说,它比SSM项目更好读懂请求流转过程。项目自带数据库脚本和工具包,配置好环境就能跑。

2. 系统功能拆解与技术选型:为什么JSP+Servlet这套老技术还能打

2.1 功能模块全览:会员、商品、积分、消费记录

这套系统解压后,先从web.xml入手看了一遍映射关系,能清楚看到它把超市会员业务拆成了几个核心模块。

模块核心功能对应Servlet/Dao
会员管理会员注册、信息查询、余额充值MemberServlet、MemberDao
商品管理商品录入、上下架、库存修改GoodsServlet、GoodsDao
消费结算会员购物结账、积分累计CheckoutServlet
积分管理积分累计、积分兑换商品/优惠ScoreServlet
系统管理管理员登录、密码修改AdminServlet

这个功能划分很典型:每个业务域对应一个Servlet接收请求,再转给DAO层操作数据库。JSP只负责展示数据,没有把业务逻辑写在页面里——这是这套源码比很多培训机构的“纯JSP项目”强的地方。会员管理是核心链路,从会员注册到消费积分再到积分兑换,闭环完整,答辩时讲解器来非常顺手。

2.2 为什么选Servlet+JDBC而不是MyBatis

现在Javaweb毕设很多用Spring Boot+MyBatis,但这套系统坚持用Servlet+JDBC,反而成了优势。至少有三点值得说:

第一,包结构一目了然。controller、dao、entity、util四个包把职责分清楚,Entity对应数据库表字段,DAO层写JDBC代码,Servlet里做请求参数封装和转发跳转。这比Spring Boot里动不动就十几层目录结构好理解得多。

第二,能清楚看到一次HTTP请求从JSP表单到Servlet再到数据库的完整路径。比如会员注册功能,JSP里form表单提交到MemberServlet的doPost方法,Servlet里调用MemberDao的addMember方法,DAO里用PreparedStatement执行SQL。这是最原始的Javaweb请求流程,面试笔试常考,答辩时老师问“数据是怎么存进去的”,直接对着代码讲就行。

第三,对做课设的学生来说,JDBC手动管理连接能让你真正理解数据库连接池存在的意义。项目里用的是DriverManager获取连接,性能上不如连接池,但对于几百条测试数据量的课程设计完全够用,而且不需要额外配置c3p0或Druid依赖,环境复杂度更低。

2.3 项目目录结构与配置文件说明

拿到项目后,理解目录结构是第一步。

SupermarketMember/ ├── src/ │ ├── com/supermarket/entity/ # 实体类:Member.java、Goods.java等 │ ├── com/supermarket/dao/ # 数据访问层:JDBC操作 │ ├── com/supermarket/servlet/ # 控制层:接收请求、跳转页面 │ ├── com/supermarket/util/ # 工具类:DBUtil.java、MD5Util.java │ └── db.properties # 数据库连接配置 ├── WebContent/ │ ├── css/ # Bootstrap、自定义样式 │ ├── js/ # jQuery、jQuery UI │ ├── images/ # 图片资源 │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述符 │ │ └── lib/ # JSTL等依赖jar包 │ └── *.jsp # 页面文件 └── sql/ └── supermarket.sql # 数据库初始化脚本

重点关注db.properties,这是数据库连接的关键配置。常见做法是把数据库驱动类、连接URL、用户名、密码都放在这个文件里,DBUtil读取Properties文件来创建连接。我一般建议学生改完这个文件后先单独写个测试类跑一下连接,不要一上来就启动Tomcat。

3. 数据库设计与初始化:从SQL脚本到关系梳理

3.1 核心数据表设计

这套系统的数据库脚本里包含了六张核心表,表间关系紧密,覆盖了超市会员业务的主体流程。第一次打开sql目录下的supermarket.sql,别急着执行,先看表结构。

-- 管理员表 CREATE TABLE admin ( admin_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '管理员ID', username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录账号', password VARCHAR(64) NOT NULL COMMENT '密码(MD5加密)' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 会员表 CREATE TABLE member ( member_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '会员ID', card_no VARCHAR(20) NOT NULL UNIQUE COMMENT '会员卡号', name VARCHAR(50) NOT NULL COMMENT '姓名', phone VARCHAR(11) COMMENT '手机号', balance DECIMAL(10,2) DEFAULT 0.00 COMMENT '账户余额', score INT DEFAULT 0 COMMENT '积分', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 商品表 CREATE TABLE goods ( goods_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品ID', goods_name VARCHAR(100) NOT NULL COMMENT '商品名称', price DECIMAL(10,2) NOT NULL COMMENT '售价', stock INT DEFAULT 0 COMMENT '库存', status TINYINT DEFAULT 1 COMMENT '1上架 0下架' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这个建表语句有几个细节值得注意:一是字符集用了utf8mb4而不是utf8,避免了生僻字和特殊符号存不进去的隐患;二是余额字段用DECIMAL(10,2)而不是FLOAT,金额计算不会出现精度误差——这一点在答辩时如果被问到“为什么不用double”,能答上来会很加分;三是会员卡号设置了UNIQUE约束,防止重复办卡。

3.2 表关系与业务流转

会员表与积分明细表、消费记录表是多对一关系。消费记录表是关键表,它关联会员和商品,结构大致如下:

CREATE TABLE consume_record ( record_id INT PRIMARY KEY AUTO_INCREMENT, member_id INT NOT NULL COMMENT '所属会员', goods_id INT NOT NULL COMMENT '购买商品', amount DECIMAL(10,2) NOT NULL COMMENT '消费金额', score_earned INT DEFAULT 0 COMMENT '获得积分', consume_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_record_member FOREIGN KEY (member_id) REFERENCES member(member_id), CONSTRAINT fk_record_goods FOREIGN KEY (goods_id) REFERENCES goods(goods_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

理解了表关系,看代码就有方向了。会员消费时做的事情是:插入一条消费记录、更新会员表里的余额和积分、扣减商品库存。这三步操作在DAO层里分别对应三个方法,设计中比较理想的做法是把它们放到一个事务里,执行过程中任何一步失败都要回滚。这套源码里的事务处理分两种写法:CheckoutServlet里的操作在Service层包裹了事务控制,而部分简单的更新操作是单条SQL执行——复现时建议重点看消费结算这条链路的事务处理。

3.3 初始化数据库的两种方式

执行SQL脚本有两种方式,我用Navicat和命令行都跑通过。

方式一,用Navicat或其他可视化工具导入:

打开Navicat,创建数据库supermarket,字符集选utf8mb4,然后右键选择“运行SQL文件”,选中项目附带的supermarket.sql。执行完毕后在表列表里能看到全部六张表以及预置的测试数据。

方式二:命令方式执行:

mysql -uroot -p123456 < sql/supermarket.sql

参数说明:-u后面跟MySQL用户名,-p后面跟密码(注意-p和密码之间没有空格)。执行后可以用SHOW TABLES;确认一下是否建表成功。

3.4 数据库连接配置与JDBC工具类

数据库建好后,修改src目录下的db.properties

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/supermarket?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456

我强烈建议你把serverTimezone=Asia/Shanghai加上去,MySQL 8.x版本如果不指定时区,连接时会直接报The server time zone value '�й���ʱ��' is unrecognized,这个报错我见过太多学生踩过。另外useSSL=false也建议保留,本地开发环境不需要SSL加密,省去证书相关的麻烦。

DBUtil工具类里的代码是老套路,加载驱动、读取Properties、创建连接:

public class DBUtil { private static String driver; private static String url; private static String username; private static String password; // 静态代码块只执行一次:加载配置 static { try { Properties props = new Properties(); InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("db.properties"); props.load(in); driver = props.getProperty("jdbc.driver"); url = props.getProperty("jdbc.url"); username = props.getProperty("jdbc.username"); password = props.getProperty("jdbc.password"); Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } // 获取连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } }

这段代码的逻辑:静态代码块在类加载时执行,把db.properties里的四项配置读进静态变量,然后Class.forName注册驱动。后续DAO层直接调用DBUtil.getConnection()就能拿到连接。

4. 项目导入与运行避坑:Eclipse+Tomcat环境下的常见问题排查

4.1 导入Eclipse的具体步骤

这套项目在Eclipse中导入时,按标准动态Web项目的方式处理。新手最容易在这一步卡住。

File -> Import -> General -> Existing Projects into Workspace,然后Select root directory选中项目根目录(就是包含pom.xml或.project文件的层级),Finish完成导入。

如果导入后没有显示成Dynamic Web Project,右键项目 -> Properties -> Project Facets,勾选Dynamic Web Module 3.0和Java 1.8,点击Apply。这一步很关键,很多同学导入后看到的是普通Java项目,JSP文件没有语法高亮,就是Project Facets没配置对。我一般会顺手检查一下Java Compiler的版本是否与JDK匹配,如果不匹配会直接导致编译失败。

4.2 Tomcat版本选择与启动

这套系统是Servlet 3.0规范,Tomcat 7及以上都能跑,但我推荐用Tomcat 8.5。Tomcat 9和10对老项目的兼容性有时会出问题,尤其是javax.servlet包名在Tomcat 10里换成了jakarta.servlet——如果项目里import的是javax.servlet,Tomcat 10直接编译报错。这是血泪经验。

# 检查Tomcat是否正常启动 sudo sh startup.sh # 或者Windows下双击startup.bat # 启动后访问 http://localhost:8080/supermarket/login.jsp

如果端口被占用,在Tomcat安装目录的conf/server.xml里修改端口:

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

把8080改成8088或其他空闲端口,改完重启Tomcat再访问http://localhost:8088/supermarket/login.jsp。

4.3 三个高频报错与解决方案

现象一:启动Tomcat时报ClassNotFoundException: com.mysql.jdbc.Driver。

原因:MySQL驱动jar包没有放到WEB-INF/lib目录下,或者放到了但没被编译。IDE里虽然能看到jar包,但运行时找不到是部署配置问题。

解决:确认WEB-INF/lib目录里有mysql-connector-java的jar包,然后在Eclipse里右键项目 -> Properties -> Deployment Assembly,点击Add -> Java Build Path Entries,把MySQL驱动jar包加进去,保存后重新发布项目。不同IDE的Project Structure里也需要确认Artifacts中是否包含了这个jar包。

现象二:页面中文乱码。

原因:jsp页面编码、MySQL连接参数、表字符集三层中至少有一层没统一。

解决:三步走。第一步把db.properties的jdbc.url加上characterEncoding=utf8;第二步确认建表时用了utf8mb4字符集;第三步在JSP页面顶部加上:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

如果还乱码,检查一下HTML的<meta charset="UTF-8">标签。这四层统一后,乱码问题基本绝迹。

现象三:修改了Java代码但运行时页面没变化。

原因:Tomcat没有热部署,或者IDE里没勾选自动编译。

解决:Eclipse里双击Servers视图中的Tomcat实例,在Server Options里勾选Serve modules without publishing和Automatically reload when resources change。改代码后手动Ctrl+S保存,再刷新浏览器。要注意的是,修改web.xml或lib目录下的jar包时还是要重启Tomcat,JSP和Java类的热部署范围是有限制的。

4.4 MySQL 8.x兼容性排查

很多学生机器上装的是MySQL 8.x,而老项目用的驱动可能是5.x版本。常见做法是换用与MySQL 8.x匹配的驱动:

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>

同时确认db.properties里的driver类名是com.mysql.cj.jdbc.Driver,不要漏掉中间的cj。MySQL 8下还要注意时区参数,这个前面提过了。另外MySQL 8默认的认证插件是caching_sha2_password,老版本驱动可能不兼容,如果连接时报Auth plugin错误,需要给MySQL用户改回兼容的加密规则:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';

这是一条非常实用的命令,写完这条再重启Tomcat,连接就回来了。

5. 核心业务代码走读:会员注册、消费结算的实现链路

5.1 会员注册:从JSP表单到DAO入库

会员注册是整个系统最基础也最完整的链路。JSP页面接收用户输入,表单提交到MemberServlet:

@WebServlet("/member/register") public class MemberRegisterServlet extends HttpServlet { private MemberDao memberDao = new MemberDao(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 设置编码,防止中文乱码 request.setCharacterEncoding("UTF-8"); // 2. 接收表单参数 String name = request.getParameter("name"); String phone = request.getParameter("phone"); String cardNo = request.getParameter("cardNo"); // 3. 封装实体对象 Member member = new Member(); member.setName(name); member.setPhone(phone); member.setCardNo(cardNo); member.setBalance(0.0); member.setScore(0); // 4. 调用DAO层入库 boolean success = memberDao.addMember(member); if (success) { response.sendRedirect(request.getContextPath() + "/member/list"); } else { request.setAttribute("errorMsg", "注册失败,请检查信息"); request.getRequestDispatcher("/member/register.jsp").forward(request, response); } } }

这段代码的要点:第一步一定要先设置request编码,否则中文姓名和手机号在参数获取阶段就乱码了;第三步手动封装实体对象,这是Servlet时代最标准的写法,后续框架里的自动参数绑定就是替代了这个枯燥的过程;第四步根据DAO层返回值决定跳转还是转发——重定向是重新发起一次请求,能避免表单重复提交,转发则保留request域里的数据用来显示错误信息。这个差异经常被问,建议记住。

MemberDao里的addMember方法,用PreparedStatement执行插入:

public boolean addMember(Member member) { String sql = "INSERT INTO member(card_no, name, phone, balance, score) VALUES(?,?,?,?,?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, member.getCardNo()); ps.setString(2, member.getName()); ps.setString(3, member.getPhone()); ps.setDouble(4, member.getBalance()); ps.setInt(5, member.getScore()); return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } }

这里用了try-with-resources语法,连接和PreparedStatement在try结束后自动关闭,不用手动写finally关了,是JDK 7引入的写法。PreparedStatement有个天然优势——预编译防SQL注入,参数用?占位,调用setString时驱动会处理特殊字符,所以不要用字符串拼接SQL,这是老项目里最常见的坑。

5.2 消费结算:事务与积分计算

消费结算是整套系统里逻辑最复杂的模块。会员购买商品时,涉及到三张表同时更新:

public boolean checkout(int memberId, int goodsId, int quantity) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交,开启事务 // 1. 查询商品信息(单价、库存) Goods goods = goodsDao.getGoodsById(conn, goodsId); double totalAmount = goods.getPrice() * quantity; // 2. 更新商品库存 int stock = goods.getStock() - quantity; goodsDao.updateStock(conn, goodsId, stock); // 3. 更新会员余额 memberDao.updateBalance(conn, memberId, totalAmount); // 4. 插入消费记录,累计积分 int earnedScore = (int)(totalAmount / 10); consumeDao.insertRecord(conn, memberId, goodsId, totalAmount, earnedScore); memberDao.updateScore(conn, memberId, earnedScore); conn.commit(); // 全部成功才提交 return true; } catch (Exception e) { e.printStackTrace(); try { conn.rollback(); // 一旦出错,全部回滚 } catch (SQLException ex) { ex.printStackTrace(); } return false; } finally { try { if (conn != null) { conn.setAutoCommit(true); conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }

事务的核心在于setAutoCommit(false)之后,所有SQL都驻留在同一个事务中,只有调用了commit才真正落盘。如果在第3步更新余额失败,第2步已经改掉了库存,这时候必须rollback把库存改回来,否则就出现“库存减了但钱没扣”的数据不一致。这是数据库事务ACID里一致性最典型的体现,答辩时讲这个例子非常加分。

积分计算规则是每消费10元累计1分,(int)(totalAmount / 10)这个写法对金额做了向下取整——消费99元得9分。这个规则在业务层写死了,如果题目要求改成“消费1元累计1分”,只需修改这一行的除数。

5.3 分页查询与模糊搜索

会员列表页用了jQuery UI和Bootstrap框架做的分页组件,后端DAO层是标准的LIMIT分页查询:

public List<Member> findMembers(String keyword, int pageNum, int pageSize) { List<Member> members = new ArrayList<>(); StringBuilder sql = new StringBuilder("SELECT * FROM member WHERE 1=1"); List<Object> params = new ArrayList<>(); if (keyword != null && !keyword.isEmpty()) { sql.append(" AND (name LIKE ? OR phone LIKE ? OR card_no LIKE ?)"); String pattern = "%" + keyword + "%"; params.add(pattern); params.add(pattern); params.add(pattern); } sql.append(" LIMIT ?, ?"); params.add((pageNum - 1) * pageSize); params.add(pageSize); try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql.toString())) { for (int i = 0; i < params.size(); i++) { ps.setObject(i + 1, params.get(i)); } ResultSet rs = ps.executeQuery(); while (rs.next()) { Member m = new Member(); m.setMemberId(rs.getInt("member_id")); m.setName(rs.getString("name")); m.setPhone(rs.getString("phone")); m.setCardNo(rs.getString("card_no")); m.setBalance(rs.getDouble("balance")); m.setScore(rs.getInt("score")); members.add(m); } } catch (SQLException e) { e.printStackTrace(); } return members; }

这里用了StringBuilder拼接SQL,两边加%实现模糊匹配。用WHERE 1=1构造动态条件,是为了后续不管有没有keyword都能直接拼接。LIMIT后面的(pageNum-1)*pageSize是分页的核心计算,第一页从第0条开始取,第二页从第10条开始取(假设pageSize=10)。这个写法要理解,答辩时老师经常会问“分页为什么是从0开始而不是从1”。

6. Tomcat部署上线技巧:把毕设从本地搬到服务器

6.1 导出WAR包手动部署

本地跑通只是第一步,答辩演示时的稳妥做法是直接在Tomcat的webapps目录下运行。在Eclipse里右键项目 -> Export -> WAR file,导出一个supermarket.war包,放到Tomcat的webapps目录下。启动Tomcat后它会自动解压部署,访问路径就是http://你的IP:8080/supermarket/

6.2 生产环境下的关键调优

本地开发环境追求能跑就行,但部署到答辩演示的服务器或虚拟机时,有些细节值得调整,也很容易在答辩时被问到。

连接池替换:在本机测几十条数据时,每次手动建连接完全可行,但一旦数据量上来或者并发访问量变大,频繁创建连接的开销不可忽视。常见做法是把源码里的DriverManager连接方式换成DBCP或Druid连接池。用Druid时,只需要在DBUtil的静态代码块里初始化一个DruidDataSource,后续getConnection时从池里拿:

private static DruidDataSource dataSource = new DruidDataSource(); static { try { dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/supermarket?..."); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setInitialSize(5); dataSource.setMaxActive(20); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }

Druid连接池的核心参数:initialSize是启动时创建的物理连接数,maxActive是最大连接数,超过这个数的新请求就要排队等待连接归还。讲课设项目时,把DriverManager替换成连接池原理说清楚,这段属于加分项。

JVM内存参数:如果Tomcat在答辩演示的过程中页面上有大表格或图片可能导致卡顿,可以在catalina.sh里调整JVM启动参数:

JAVA_OPTS="-Xms512m -Xmx1024m -XX:MaxPermSize=256m"

这个参数的意义:JVM初始分配512MB堆内存,最大允许1024MB。堆内存过小时频繁触发Full GC会导致页面卡顿,设大一些能缓解大数据量加载时的压力。

MySQL乱码一劳永逸方案:修改MySQL配置文件my.ini(Windows)或my.cnf(Linux)底部的[mysqld]段落,加入:

[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci

改完后重启MySQL服务,新创建的数据库默认就是utf8mb4,不需要每条建表语句都手动指定字符集。这个统一配置比在JDBC连接上加characterEncoding参数更彻底,因为后者只对当前连接生效,前者对所有连接生效。

6.3 答辩演示前的验证清单

最后分享一个我每次上线前都会跑一遍的检查流程。先是登录管理员账号,试着新增一个会员,再去消费结算,最后看记录;把窗口关掉重开,确认数据还在——验证持久化没问题。改掉db.properties里的数据库密码,重启项目,确认正常用新密码连接——这一步是为了防止答辩时换了机器漏改了配置。再确认一下WEB-INF/lib目录下的mysql驱动版本与服务器MySQL版本匹配,MySQL 8就配8.0.x的驱动,别混用。这套流程走下来大概也就十分钟,但能避开九成以上的现场翻车。从那以后,我每次帮学生部署毕设项目,都强制自己先跑一遍这套验证清单再收工。希望这些细节能帮到你,少走点我当年走过的弯路。

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

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

多因素认证与TOTP:身份认证令牌的选型、原理与落地

身份认证令牌这几年在后台系统、金融App、企业内部系统里出现得越来越频繁&#xff0c;我自己第一次真正动手接入身份认证令牌&#xff0c;是在给一个内部运维平台做登录改造的时候。当时团队正在被“密码疲劳”折磨——每个人的密码规则越来越多&#xff0c;改密周期越来越短&…

作者头像 李华
网站建设 2026/9/24 19:44:17

QQ音乐Hi-Res音源解析原理与合规下载实践

1. 项目概述&#xff1a;这不是“破解”&#xff0c;而是对音频服务协议的合规性技术复现最近在几个音乐技术交流群里&#xff0c;频繁看到有人问&#xff1a;“有没有能下QQ音乐无损音质的工具&#xff1f;”“1951版还能用吗&#xff1f;”“MFLAC转MP3怎么不丢质量&#xff…

作者头像 李华
网站建设 2026/9/24 19:44:05

MySQL架构核心:存储引擎、主从复制与分库分表实战解析

如果你接手过一套正在线上跑的MySQL架构&#xff0c;或者正在准备MySQL方向的面试&#xff0c;那存储引擎、主从复制、分库分表这三块内容迟早要碰到。我自己就是被真实故障教育过的人&#xff1a;第一次是MyISAM的表锁导致全站请求排队&#xff0c;第二次是主从延迟让报表数据…

作者头像 李华
网站建设 2026/9/24 19:43:47

查找替换效率对比:Notepad3与VSCode正则实战技巧

我以前常年在几个编辑器之间横跳&#xff0c;系统记事本、Notepad、Notepad3、VSCode 都用过。改配置、清洗日志、批量修代码的时候&#xff0c;查找替换用得最频繁。说真的&#xff0c;同样是查找替换&#xff0c;不同编辑器的体验差距非常大。尤其是 Notepad3 原生的查找替换…

作者头像 李华
网站建设 2026/9/24 19:43:37

基于YOLOv8的道路标线磨损监测:从环境搭建到可视化部署

简介&#xff1a;面向毕业设计或课程设计的一套YOLOv8交通道路标线磨损监测系统&#xff0c;完整覆盖数据准备、模型训练、目标检测与可视化展示全流程&#xff0c;适合计算机相关专业学生和开发者快速落地项目。代码均已测试运行成功&#xff0c;内置可视化页面、完整数据集与…

作者头像 李华
网站建设 2026/9/24 19:42:56

网吧盈利现状与未来潜力深度全景解读(超万字攻略)

一、前言&#xff1a;网吧&#xff0c;从辉煌到转型的世纪旅程 曾几何时&#xff0c;网吧是无数80后、90后青春的记忆——“上网两小时&#xff0c;快乐一整天”&#xff0c;在那个互联网刚起步的年代&#xff0c;网吧是信息的窗口&#xff0c;是游戏的乐园&#xff0c;也是城市…

作者头像 李华