B/S网络投票系统实战:Spring Boot+MySQL防刷与唯一索引设计

简介:浏览器/服务器架构的网络投票系统源码,面向中小型组织或开发者,用于在线发起投票、收集结果并完成统计,支持并发数在100以内,可在合理范围内保证投票流程流畅。资源共56个文件,以26个ASP动态页面为核心,配合GIF/JPG界面图片、CSS样式表、JS脚本及MDB数据库,构成完整的前后台运行环境;压缩包仅116KB,轻量小巧,便于快速下载与部署。后台功能覆盖投票主题创建、选项增删改、系统配置调整以及管理员登录鉴权,另附安装方法、功能简介等文本说明;从文件结构可看出包含投票添加、编辑、选项管理和后台登录等独立模块,为二次开发提供清晰参考。已有197人浏览学习,适合学习经典ASP开发模式、理解浏览器与服务器数据交互流程,或需要搭建轻量级投票系统的开发者参考。

1. B/S网络投票系统是什么:从一次活动评选的翻车说起

网络投票系统这几年在课程设计和企业内部活动里几乎成了标配,但多数人第一次接触它,不是因为想学技术,而是被纸质统计逼到绝路。某家公司年度评优,第一年用纸质票加人工统计,票数怎么都对不上账,负责人熬到深夜。第二年换成浏览器访问的B/S网络投票系统:员工打开链接就能投,后台实时出结果,防重复、防刷票,账目清清楚楚。这套系统说白了就是浏览器/服务器架构——前端负责展示选项和提交动作,后端负责鉴权、记票和统计,数据库负责落账。它适合高校课设、企业评优、社区评选这类限时、限人、可统计的公开投票场景。下面按实际拆这类资源的经验,把架构、防刷和踩坑讲透。

2. 技术选型与工程结构:Spring Boot + MySQL,以及为什么劝你别上微服务

2.1 技术栈的选择边界

B/S投票系统市面上有各种变体:纯Servlet加JSP的老方案、PHP一页流、Node.js快跑流,还有把前端后端拆成两个工程再上网关的"大炮打蚊子"方案。我的建议很直接:后端用Spring Boot,前端用模板渲染加少量原生JavaScript,数据库用MySQL,不要上前后端分离,也不要碰微服务。

为什么这么选?先说Spring Boot,内置Tomcat,打成一个jar包扔到服务器上就能跑,这对课程设计和中小型业务是最低成本的部署路径。Spring MVC把路由、参数绑定、JSON序列化全部内置,你只需要关注Controller、Service、Mapper三层,心智负担小得多。数据库用MySQL还有一个现实考虑:这份资源的建表SQL、备份脚本、部署文档都是按MySQL写的,换库意味着所有语法和驱动都要重来一遍。PostgreSQL的功能确实更强,但在这个场景里用到的行级锁、唯一索引、日期函数,MySQL全都具备;SQLite更轻,但并发写能力弱,投票场景写入频繁,不适合。

再说为什么劝你别上微服务。投票系统本质上是"读多写少"的单体应用,高峰期也就几百到几千并发,单体模式用一个MySQL实例加个Redis缓存就能扛住。微服务引入的服务注册、配置中心、分布式事务,任何一个都会让你在调试阶段多花三倍时间,而且课设答辩时被问到"为什么拆这么细",很难讲清楚业务上的必要性。前端用Thymeleaf模板把页面直接渲染出来,投票列表、结果图表用原生JS加ECharts就够了。不搞Vue/React前后端分离,意味着少维护一套部署、少处理跨域、少写一套鉴权适配,对这份资源的使用者来说,多出来的复杂度远大于收益。

2.2 包结构与三层分层

源码的包结构我习惯按功能模块组织,而不是按纯技术层堆目录,这样后期加"多主题投票"功能时不会把代码改乱:

src/main/java/com/vote/ ├── controller/ │ ├── AuthController.java │ ├── VoteController.java │ └── AdminController.java ├── service/ │ ├── VoteService.java │ ├── AuthService.java │ └── StatService.java ├── mapper/ │ ├── VoteRecordMapper.java │ ├── VoteSubjectMapper.java │ └── VoteOptionMapper.java ├── entity/ │ ├── VoteSubject.java │ ├── VoteOption.java │ └── VoteRecord.java ├── common/ │ ├── Result.java │ ├── GlobalExceptionHandler.java │ └── IpUtils.java └── config/ └── AuthInterceptor.java

这个分层的边界就三句话:Controller只做"收参和吐结果",不写业务判断;Service承载所有规则,比如投票时间校验、重复校验、票数落账;Mapper只对应SQL,不出现业务逻辑。很多新手喜欢在Controller里写一大坨if判断,最后Service层是空的,接口一多就难维护。我接手过的某个课设项目把所有逻辑堆在Controller里,连数据库连接都写在方法内,演示时一张票投了三次才暴露问题——重复校验和插入不是原子的,中间隔了几行耗时日志,并发双击就穿透了。

这套包结构在只有一个投票主题的场景里看起来有一点重,但三个Controller对应的是三种完全不同的调用方:普通用户端、后台管理端、统计展示端。分开之后权限控制才好做,比如给AdminController单独加管理员角色校验,给统计接口加只读权限,不会被一个入口的拦截器搅在一起。

2.3 接口约定与统一响应

前后端不管是否分离,接口约定要先定死。我用一套极简约定:

方法路径作用是否需要登录
POST/api/auth/login登录拿token否
GET/api/subject/detail获取投票主题与选项是
POST/api/vote/submit提交投票是
GET/api/stat/detail获取实时统计是
POST/api/admin/create管理员创建主题管理员

所有接口返回统一结构Result<T>:

public class Result<T> { private int code; // 0表示成功,非0为业务错误码 private String msg; // 错误描述,成功时固定为"ok" private T data; // 业务数据 public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.code = 0; r.msg = "ok"; r.data = data; return r; } public static <T> Result<T> error(int code, String msg) { Result<T> r = new Result<>(); r.code = code; r.msg = msg; return r; } }

这里的code不是HTTP状态码,而是业务错误码,比如1001表示投票未开始,1002表示投票已结束,1003表示重复投票,1004表示登录态失效。HTTP状态码只有几位,承载不了"重复投票"和"投票已结束"这种细粒度差异,前端拿到code直接弹对应提示,不用去解析msg字符串。登录用token模式,登录成功后签发token存在Redis里,有效期设为活动时长,前端每次请求带在Header里。

token校验写在拦截器里:

public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); String key = "vote:token:" + token; if (token == null || !redisTemplate.hasKey(key)) { writeError(response, 1004, "登录态失效"); return false; } request.setAttribute("userId", redisTemplate.opsForValue().get(key)); return true; } }

拦截器的逻辑是先从Header拿token,拼接Redis的key并判断是否存在,同时把userId放进request属性,后续Controller直接取值,不用再查一次Redis。注意hasKey判断语义更清晰,但如果接口QPS很高,改为直接get判空可以省一次Redis往返,两种写法都可以,按对性能的敏感度取舍。

3. 数据库设计与防刷底层:五张表 + 唯一索引兜底

3.1 核心表结构设计

投票系统的数据库模型,核心不是"一张票表",而是多个相互关联的表。这里最核心的是投票主题表、投票选项表、投票记录表三张。先看投票主题表:

CREATE TABLE `vote_subject` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '投票主题ID', `title` VARCHAR(100) NOT NULL COMMENT '投票标题', `description` TEXT COMMENT '投票说明', `start_time` DATETIME NOT NULL COMMENT '开始时间', `end_time` DATETIME NOT NULL COMMENT '结束时间', `max_select` INT DEFAULT 1 COMMENT '每人最多可选几项,单选为1', `status` TINYINT DEFAULT 1 COMMENT '0草稿 1发布 2结束', KEY `idx_time` (`start_time`, `end_time`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='投票主题表';

然后是选项表:

CREATE TABLE `vote_option` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `subject_id` BIGINT NOT NULL COMMENT '所属主题ID', `option_name` VARCHAR(200) NOT NULL, `sort_order` INT DEFAULT 0 COMMENT '展示顺序,越小越靠前', KEY `idx_subject` (`subject_id`), CONSTRAINT `fk_option_subject` FOREIGN KEY (`subject_id`) REFERENCES `vote_subject` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='投票选项表';

最后是投票记录表,这是整个系统的防刷核心:

CREATE TABLE `vote_record` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `subject_id` BIGINT NOT NULL, `option_id` BIGINT NOT NULL, `user_id` BIGINT NOT NULL, `ip` VARCHAR(64) DEFAULT NULL COMMENT '投票来源IP', `device_id` VARCHAR(64) DEFAULT NULL COMMENT '设备指纹', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY `uk_user_subject` (`user_id`, `subject_id`), KEY `idx_subject_option` (`subject_id`, `option_id`), KEY `idx_ip` (`ip`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='投票记录表';

几个关键设计说明。第一,max_select字段决定单选还是多选,很多票数对不上的翻车案例就是对它没做参数校验。第二,ID用BIGINT自增主键,不用UUID,因为记录表插入频繁,UUID主键在InnoDB里会导致页分裂,写入性能明显下降。第三,字符集在建表时就定成utf8mb4,不要留到建库之后才想起来——选项名称里混入生僻字或Emoji时,utf8mb3会直接报错。

提示:字符集在建库时定成utf8mb4,如果表建好之后才改,生产环境要跑ALTER TABLE,期间会锁全表,数据量大时风险很高。

3.2 唯一约束与并发控制

重复投票是投票系统里最隐蔽的Bug,很多初版实现只在应用层做一次"查一下有没有投过"的判断。问题是并发场景下,两个请求同时通过"未投票"检查,然后各自插入一条记录,应用层的查询根本防不住。数据库层的唯一索引才是兜底方案——uk_user_subject在数据库层面保证同一个用户对同一个主题只能有一条记录,即使应用层漏了,第二次插入也会被数据库拒绝并抛DuplicateKeyException。

有了唯一索引,应用层只需捕获这个异常再转成"您已参与过投票"的提示,逻辑反而更干净。这里有个必看的陷阱:InnoDB下唯一索引冲突后的插入会等待行锁,极端情况下会拉长响应时间。所以代码里要配合事务和死锁重试,下面第4章会展开。

并发场景下另一个风险是更新选项票数的丢更新问题。如果先"SELECT vote_count FROM vote_option WHERE id=?"再把值加1写回去,两个并发请求就会互相覆盖。正确姿势是直接用一条原子UPDATE完成计数:

UPDATE vote_option SET vote_count = vote_count + 1 WHERE id = #{optionId}

这个语句由存储引擎保证原子性,不需要额外加锁。很多人习惯"先读后写"是受了ORM思维影响,在这个场景里直接写SQL反而更稳。

3.3 防刷策略的分层

防刷不是一个单点技术能解决的,我的习惯是分四层:验证码层、IP限制层、用户登录层、数据库唯一索引层。

验证码层防最粗粒度的脚本刷票。投票前必须完成算术验证码,比如随机生成两个两位数让用户算加法。算术验证码生成成本低,OCR反而不容易读准;正确答案只存Redis并设置2分钟过期,校验一次就删除,防止重复重放。这里有个容易忽略的细节:验证码生成的接口也要限流,否则别人可以并发请求刷验证码打爆Redis内存。

IP限制层针对未登录或匿名投票场景。注意一个很坑的细节:投票系统往往部署在公司内网或校园网,NAT后面的所有用户共享同一个出口IP。如果把IP限流做成"一个IP只能投一票",一个办公室的人会互相把对方挡在外面。正确做法是"一个IP在一个时间窗口内允许N个不同用户投票",比如5分钟内允许10个不同user_id,超出才拦截。实现上可以用Redis维护一个IP维度的集合,value存user_id,用EXPIRE设置窗口,投票前检查集合数量是否超限。

用户登录层解决"一人一票"的语义问题。用user_id结合唯一索引,才是防重复投票的真正闸门。匿名投票系统难防刷,本质是找不到稳定的唯一身份锚点,所以凡是业务上允许,我都建议加上手机号或学工号登录。数据库唯一索引层是最后一道闸门,解决的是并发绕路的问题——有人直接抓接口绕过前端连续POST一百次,只要user_id相同,唯一索引能拦下绝大多数。

4. 核心链路实现:鉴权、投票、统计的代码手把手

4.1 登录鉴权怎么做

登录接口用Spring Boot加Redis做token,Redis自带过期时间,token失效后直接删除,不用维护额外的session存储。代码如下:

@Service public class AuthService { @Autowired private UserMapper userMapper; @Autowired private StringRedisTemplate redisTemplate; private static final String TOKEN_PREFIX = "vote:token:"; public String login(String account, String password) throws Exception { // 1. 先做基础参数校验,防止空指针和超长字符串 if (account == null || account.isBlank() || password == null || password.isBlank()) { throw new BizException(1000, "账号或密码不能为空"); } // 2. 查用户表,比对密码哈希 User user = userMapper.selectByAccount(account); if (user == null) { throw new BizException(1001, "账号不存在"); } // 这里用BCrypt校验,不要存明文密码 if (!BCrypt.checkpw(password, user.getPasswordHash())) { throw new BizException(1002, "密码错误"); } // 3. 生成token并写入Redis,有效期设为活动时长 String token = UUID.randomUUID().toString().replace("-", ""); long expireSeconds = voteConfig.getDurationHours() * 3600L; redisTemplate.opsForValue().set(TOKEN_PREFIX + token, String.valueOf(user.getId()), expireSeconds, TimeUnit.SECONDS); return token; } }

代码里三处需要按项目调整。第一,密码存的是BCrypt哈希而不是明文,表结构中password_hash字段长度要留到60位以上,否则保存时报Data too long。第二,Redis的key加了前缀"vote:token:",后续要用SCAN命令清理登录态时不会误删其他业务数据。第三,过期时间直接用活动时长,活动结束后登录态自动失效,不用跑定时任务刷新。

4.2 投票提交核心代码

投票提交是整个系统最敏感的一段,Service层代码:

@Transactional(rollbackFor = Exception.class) public void submitVote(Long userId, Long subjectId, List<Long> optionIds) { // 1. 校验投票主题是否存在并处于可投票时间段 VoteSubject subject = subjectMapper.selectById(subjectId); if (subject == null) { throw new BizException(2001, "投票主题不存在"); } LocalDateTime now = LocalDateTime.now(); if (now.isBefore(subject.getStartTime())) { throw new BizException(2002, "投票尚未开始"); } if (now.isAfter(subject.getEndTime())) { throw new BizException(2003, "投票已结束"); } // 2. 校验选项数不能超过max_select,防止一次提交几十个选项 if (optionIds == null || optionIds.isEmpty()) { throw new BizException(2004, "请至少选择一个选项"); } if (optionIds.size() > subject.getMaxSelect()) { throw new BizException(2005, "最多只能选择" + subject.getMaxSelect() + "项"); } // 3. 应用层查重,防御性判断,并发兜底交给数据库唯一索引 int existCount = voteRecordMapper.countByUserSubject(userId, subjectId); if (existCount > 0) { throw new BizException(2006, "您已参与过投票"); } // 4. 插入记录,并原子更新选项票数 for (Long optionId : optionIds) { VoteRecord record = new VoteRecord(); record.setUserId(userId); record.setSubjectId(subjectId); record.setOptionId(optionId); voteRecordMapper.insert(record); voteOptionMapper.increaseVoteCount(optionId); } }

关键在第四步,插入记录和更新票数在同一个事务里完成。单选项投票用一条INSERT加一条UPDATE;多选时循环处理,任何一条失败整个事务回滚,不会出现"记录插了但票数没加"的脏数据。

那并发双击怎么防?两个请求都通过了第三步的查重,然后同时进入第四步。此时数据库的uk_user_subject唯一索引会让其中一条插入失败,抛出DuplicateKeyException,事务回滚。Controller层要捕获这个异常并转成2006错误码返回给前端,不要让它变成500。

注意:@Transactional注解一定要指定rollbackFor = Exception.class,否则抛出自定义业务异常时事务默认不会回滚,会出现记录没插但票数已加一半的错误。

第三步的查重和第四步的插入之间存在一个时间窗口,想彻底消除只能靠数据库唯一索引。如果建表时没建唯一索引,这段代码不管怎么写都防不住并发重复。

4.3 统计接口的SQL与缓存

统计接口是高频读接口,投票一旦开放,刷新频次会非常高。逻辑分两步:先从数据库查原始聚合结果,再把结果缓存到Redis并设置5秒过期,数据库压力至少降一个数量级。

SELECT o.id AS option_id, o.option_name, COUNT(v.id) AS vote_count FROM vote_option o LEFT JOIN vote_record v ON v.option_id = o.id WHERE o.subject_id = #{subjectId} GROUP BY o.id, o.option_name ORDER BY vote_count DESC, o.sort_order ASC

这里用LEFT JOIN而不是INNER JOIN,目的是把票数为0的选项也显示出来。注意GROUP BY后面必须把SELECT里出现的非聚合列全部列全,否则在MySQL的ONLY_FULL_GROUP_BY模式下会直接报错,这是低版本MySQL经常翻车的点。

ORDER BY有个坑:先按vote_count降序,再按sort_order升序。如果只按票数排序,票数相同的选项每次刷新顺序都在跳,演示时观感很差。缓存直接用String存JSON,key为"vote:stat:subjectId",过期5秒。为什么不设更长?因为投票结果需要实时感,5秒延迟观众感知不到,数据库压力已经被削平。等人气上来,再考虑定时任务每10秒刷一次统计,而不是每次请求都打数据库。

5. 踩坑记录:网络投票系统最容易翻车的五个地方

5.1 同一局域网的所有用户被IP限流拦死

现象:公司内网里第一个投票的人正常,第二个同事一投就被提示"操作太频繁",然后全办公室都投不了了。

原因:IP限流做成了"单IP单票"。公司网络走NAT出口,所有人对外都是同一个IP,第一个投完就把整个出口IP封了。

解决:把IP限流改成时间窗口加数量阈值,比如同一个IP在5分钟内允许来自最多10个不同user_id的投票请求,超出才拦截。Redis里用ZSet维护IP维度的集合,value存user_id,每次投票前清掉过期元素再检查数量。

5.2 并发双击时一个用户投出两票

现象:演示现场有人快速双击投票按钮,后台记录里同一个用户出现两条记录,票数虚增。

原因:前端按钮禁用了,但后端只做了"先查后插"的应用层判断,两个并发请求同时通过检查,同时插入;数据库没有唯一索引兜底。

解决:前端禁用按钮只是给正常人用的,真正防住必须靠vote_record表上的(user_id, subject_id)唯一索引,Controller捕获DuplicateKeyException转成友好提示。那次翻车之后,我每建一张能落账的表都会先想一遍:同一个请求来两遍,数据库会怎么处理。

5.3 多选投票的总票数和参与人数对不上

现象:活动规则是每人可投3项,统计结果总票数是900,但参与人数只有300,对不上账。

原因:统计接口直接用COUNT(vote_record.id)算总数。多选场景下,每个用户会产生多条记录,总数算出来的是"票项数"而不是"人数"。

解决:固定用两种口径对账:统计参与人数用COUNT(DISTINCT user_id),统计票项总数用COUNT(id),两个数分别展示并在前端标注清楚。如果业务方坚持只展示总票数,也要在说明文案里写明"每人可选多项",否则会被质疑注水。

5.4 缓存过期瞬间被刷票

现象:投票高峰期突然连续进来几十个重复投票请求,日志里全是"您已参与过投票"。

原因:把"用户已投票"标记存在Redis里,并设置了较短的过期时间。标记过期后用户又能重新进入投票流程,如果数据库唯一索引没建或者校验不严格,就真的投进去了。

解决:缓存只能当快速拦截层,判定最终能不能投必须走数据库唯一索引。缓存过期只影响性能,不影响正确性。类似的坑还有验证码:验证码答案可以带过期时间,但投票资格不能带——这是两个语义完全不同的东西,别写在一个工具类里。

5.5 投票高峰期接口变慢、数据库CPU飙高

现象:活动开始前半小时涌入大量请求,接口响应从几十毫秒变成几秒,数据库CPU接近100%。

原因:慢日志查出来是热门的统计SQL和查重查询在跑全表扫描。选项表、记录表上的索引不匹配查询条件。

解决:给常用过滤条件建联合索引,特别是vote_record表的(user_id, subject_id)唯一索引本身就是查重的索引;统计SQL对应的LEFT JOIN条件加(subject_id, option_id)索引,让查询能走覆盖索引。统计接口再套一层Redis缓存,设置5秒过期。Tomcat最大线程数从默认200调小到100,配合数据库连接池上限,宁可让请求排队,也不要让数据库被短连接淹没。

6. 上线前的最后一件事:压测与调优的两个实用动作

6.1 JMeter压测脚本与指标解读

先做一个最简单的压测:用JMeter建一个线程组,模拟50个用户同时登录并投票,每线程循环10次,观察接口的响应时间分布和错误率。重点看两个指标:TP95和错误率,TP95超过500毫秒就说明有瓶颈需要排查。

压测时要做的第一个动作是关掉Redis缓存再压一次。如果关闭缓存后接口性能大幅下降,说明你压到的其实是缓存命中的结果,数据库的真实水位还没暴露,生产环境一旦缓存抖动,后果就是接口超时。

6.2 慢查询日志与索引诊断

压测完打开MySQL慢查询日志:

SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

把超过1秒的SQL全部捞出来,用EXPLAIN看执行计划,重点看type字段是不是ALL或index。这两个值表示全表扫描,需要针对WHERE和JOIN条件建联合索引。真实项目里我遇到过一条统计SQL因为索引缺失,压测时执行了3.2秒,加完索引后直接降到40毫秒,投入产出比极高。

最后说一个习惯:从那以后我每上线一个B/S系统,都会强制走一遍"压测 → 慢日志 → 索引诊断 → 缓存核对"四步流程。这次拆网络投票系统最大的收获不是代码本身,而是明白了防刷必须靠数据库唯一索引这种"事实约束",而不是靠应用层逻辑去堵。源码、建表SQL和部署文档都在资源包里,按第4章的接口清单就能跑通全流程。希望帮到你。

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

相关新闻

SSM与Django双版本订餐系统开发实战:从数据库设计到部署交付

SSM与Django双版本订餐系统开发实战:从数据库设计到部署交付

1. 项目缘起&#xff1a;这套订餐系统到底做了什么我最近刚结束一个颇为典型的项目——给某高校做的一套订餐管理系统&#xff0c;完整交付内容包括源码、论文文档、调试文档和讲解演示。这套系统的亮点在于同时给甲方做了Java SSM版和Python Django版两套后端&#xff0c;前端…

2026/10/22 13:34:41 阅读更多 →
从UMAP准确标注目标基因对应的细胞类群:Seurat与scanpy实操指南

从UMAP准确标注目标基因对应的细胞类群:Seurat与scanpy实操指南

做单细胞转录组的人&#xff0c;几乎都遇到过这个场景&#xff1a;聚类跑完&#xff0c;UMAP也画出来了&#xff0c;clusters都标好了&#xff0c;然后项目组里抛过来一个问题——“我们感兴趣的那个基因&#xff0c;到底在哪群细胞里&#xff1f;能不能在UMAP图上一眼标出来&a…

2026/10/22 13:57:54 阅读更多 →
如何系统盘点个人技能:三层结构、0-5级量表与提升路径

如何系统盘点个人技能:三层结构、0-5级量表与提升路径

先说结论&#xff1a;这是一件大多数人根本没做对的事“skills”这个词&#xff0c;估计是简历里出现频率最高的单词。招聘JD里写“requirements”&#xff0c;候选人就在自我评价里罗列七八项技能&#xff1b;年终复盘的时候&#xff0c;大家对着空白的表格发呆&#xff0c;想…

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

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年&#xff0c;我大部分时间都在跟浏览器标签页打交道&#xff0c;直到第一次把桌面应用跑起来&#xff0c;才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词&#xff0c;最开始是在某段时间频繁出现在同事的讨论里&#xff0c;一个用HTML…

2026/10/22 10:18:38 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档&#xff0c;主要面向智能锁安装人员、终端用户及售后维护者&#xff0c;帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件&#xff0c;大小4.37MB&#xff0c;内容结构清晰&#xff…

2026/10/22 11:01:53 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统&#xff0c;整车要求空载跑到8m/s&#xff0c;电机转速已经拉到额定点&#xff0c;驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令&#xff0c;转速纹丝不动&#xff0c;三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/22 9:44:47 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年&#xff0c;用它写过嵌入式驱动、做过游戏服务端&#xff0c;也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词&#xff0c;我其实挺感慨的——这门语言的难点从来不是语法多&#xff0c;而是知识点之间的耦合太深…

2026/10/22 10:42:00 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书以PDF格式发布&#xff0c;适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开&#xff0c;先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能&#xff0c;再给出完整安装步骤&…

2026/10/22 10:40:08 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历&#xff1a;翻开源码注释、论文摘要或者一份专业报告&#xff0c;每个字都认识&#xff0c;连在一起却像天书。想找人聊聊&#xff0c;身边要么没人懂&#xff0c;要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型&#xff0c;几秒钟之后&a…

2026/10/22 10:56:37 阅读更多 →

日新闻

周新闻

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

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

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

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

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

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

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

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

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

2026/10/22 5:12:39 阅读更多 →

月新闻

我发现了一个新思路:用 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/21 21:50:56 阅读更多 →
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/21 21:51:55 阅读更多 →
黑夜航拍船只数据集训练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/21 16:24:48 阅读更多 →