基于Spring Boot与Redis的分布式投票系统设计与实现
最近在技术社区里很多开发者都在讨论如何构建更智能的投票系统。传统的投票方案往往只关注简单的票数统计但在实际项目中我们经常需要处理更复杂的场景如何防止刷票如何确保投票结果的公正性如何在分布式环境下保证数据一致性今天要介绍的冠亚投票系统正是为了解决这些痛点而设计的。与传统的简单投票不同冠亚投票系统采用了多层验证机制和分布式锁方案能够有效应对高并发场景下的数据一致性问题。本文将带你从零开始实现一个完整的冠亚投票系统涵盖从架构设计到代码实现的完整流程。1. 冠亚投票系统的核心价值与适用场景冠亚投票系统不仅仅是一个简单的票数统计工具它的核心价值在于解决了传统投票系统中的几个关键问题技术痛点分析数据一致性难题在高并发场景下多个用户同时投票可能导致票数统计不准确安全性挑战缺乏有效的防刷票机制容易被恶意攻击性能瓶颈传统数据库在大量并发写入时容易出现性能问题结果可信度简单的票数统计无法提供足够的审计轨迹适用场景技术社区的技术方案评选开源项目的功能优先级投票团队内部的技术决策产品功能的需求收集与排序2. 系统架构设计与技术选型2.1 整体架构概览冠亚投票系统采用微服务架构主要包含以下核心组件用户界面层 → API网关 → 投票服务 → 数据存储层 ↓ ↓ 认证服务 缓存层2.2 技术栈选择理由后端框架Spring Boot 3.x成熟的生态系统和丰富的中间件支持内置的监控和健康检查功能与分布式组件良好集成数据库Redis MySQL组合Redis处理高并发投票请求作为缓存层MySQL持久化存储投票结果和审计日志分布式锁Redisson基于Redis的分布式锁实现支持可重入锁和公平锁自动续期机制防止死锁3. 环境准备与依赖配置3.1 开发环境要求# 检查Java版本 java -version # 要求JDK 17或更高版本 # 检查Maven版本 mvn -version # 要求Maven 3.6 # 检查Docker环境可选用于本地测试 docker --version3.2 项目依赖配置!-- pom.xml -- ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.0/version /parent dependencies !-- Web支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Redis支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- Redisson分布式锁 -- dependency groupIdorg.redisson/groupId artifactIdredisson-spring-boot-starter/artifactId version3.23.2/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency /dependencies /project3.3 配置文件设置# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/vote_system username: vote_user password: your_password driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 password: your_redis_password database: 0 jackson: time-zone: GMT8 server: port: 8080 # 投票系统配置 vote: system: max-votes-per-user: 3 vote-duration-hours: 24 result-calculation-delay: 30004. 核心数据模型设计4.1 数据库表结构-- 投票主题表 CREATE TABLE vote_topic ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL COMMENT 投票主题, description TEXT COMMENT 投票描述, start_time DATETIME NOT NULL COMMENT 开始时间, end_time DATETIME NOT NULL COMMENT 结束时间, max_choices INT DEFAULT 1 COMMENT 最大选择数, status TINYINT DEFAULT 0 COMMENT 状态0-未开始 1-进行中 2-已结束, created_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 投票选项表 CREATE TABLE vote_option ( id BIGINT PRIMARY KEY AUTO_INCREMENT, topic_id BIGINT NOT NULL COMMENT 关联主题ID, option_text VARCHAR(100) NOT NULL COMMENT 选项内容, display_order INT DEFAULT 0 COMMENT 显示顺序, FOREIGN KEY (topic_id) REFERENCES vote_topic(id) ); -- 投票记录表用于审计 CREATE TABLE vote_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, topic_id BIGINT NOT NULL, option_id BIGINT NOT NULL, user_id VARCHAR(50) NOT NULL COMMENT 用户标识, vote_time DATETIME DEFAULT CURRENT_TIMESTAMP, ip_address VARCHAR(45) COMMENT IP地址用于防刷, user_agent TEXT COMMENT 用户代理信息, INDEX idx_topic_user (topic_id, user_id), FOREIGN KEY (topic_id) REFERENCES vote_topic(id), FOREIGN KEY (option_id) REFERENCES vote_option(id) );4.2 Java实体类设计// VoteTopic.java Entity Table(name vote_topic) public class VoteTopic { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(nullable false, length 200) private String title; Column(columnDefinition TEXT) private String description; Column(name start_time, nullable false) private LocalDateTime startTime; Column(name end_time, nullable false) private LocalDateTime endTime; Column(name max_choices) private Integer maxChoices 1; private Integer status 0; Column(name created_time) private LocalDateTime createdTime LocalDateTime.now(); // getters and setters } // VoteOption.java Entity Table(name vote_option) public class VoteOption { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(name topic_id) private Long topicId; Column(name option_text, nullable false, length 100) private String optionText; Column(name display_order) private Integer displayOrder 0; // getters and setters }5. 核心投票逻辑实现5.1 投票服务核心类Service Slf4j public class VoteService { Autowired private RedissonClient redissonClient; Autowired private RedisTemplateString, Object redisTemplate; Autowired private VoteRecordRepository voteRecordRepository; /** * 执行投票操作 */ public VoteResult vote(VoteRequest request) { // 获取分布式锁防止重复投票 RLock lock redissonClient.getLock(vote_lock: request.getTopicId() : request.getUserId()); try { // 尝试获取锁等待5秒锁有效期30秒 if (lock.tryLock(5, 30, TimeUnit.SECONDS)) { return doVote(request); } else { throw new VoteException(系统繁忙请稍后重试); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new VoteException(投票操作被中断); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } private VoteResult doVote(VoteRequest request) { // 1. 验证投票有效性 validateVote(request); // 2. 记录投票到数据库审计 recordVoteToDB(request); // 3. 更新Redis中的实时票数 updateRedisVoteCount(request); // 4. 返回投票结果 return buildVoteResult(request); } private void validateVote(VoteRequest request) { // 检查投票时间是否有效 VoteTopic topic getVoteTopic(request.getTopicId()); if (topic.getStatus() ! 1) { throw new VoteException(投票未开始或已结束); } // 检查用户是否超过最大投票次数 long userVoteCount voteRecordRepository.countByTopicIdAndUserId( request.getTopicId(), request.getUserId()); if (userVoteCount topic.getMaxChoices()) { throw new VoteException(已达到最大投票次数); } // 检查IP频率限制 checkIpRateLimit(request.getIpAddress()); } }5.2 防刷票机制实现Component public class AntiSpamService { private static final String IP_RATE_LIMIT_KEY ip_rate_limit:%s; private static final int MAX_REQUESTS_PER_MINUTE 10; Autowired private RedisTemplateString, Object redisTemplate; public void checkIpRateLimit(String ipAddress) { String key String.format(IP_RATE_LIMIT_KEY, ipAddress); Long count redisTemplate.opsForValue().increment(key, 1); // 如果是第一次访问设置过期时间 if (count ! null count 1) { redisTemplate.expire(key, 1, TimeUnit.MINUTES); } if (count ! null count MAX_REQUESTS_PER_MINUTE) { throw new VoteException(操作过于频繁请稍后重试); } } /** * 用户行为分析防刷票 */ public void analyzeUserBehavior(String userId, String ipAddress) { // 分析用户投票模式 String behaviorKey user_behavior: userId; MapObject, Object behavior redisTemplate.opsForHash().entries(behaviorKey); // 记录投票时间间隔、选项分布等模式 // 异常模式检测逻辑... } }6. 实时票数统计与结果计算6.1 Redis计数器实现Service public class VoteCountService { Autowired private RedisTemplateString, Object redisTemplate; /** * 更新选项票数 */ public void incrementVoteCount(Long topicId, Long optionId) { String key getVoteCountKey(topicId); redisTemplate.opsForHash().increment(key, optionId.toString(), 1); // 设置过期时间投票结束后保留7天 redisTemplate.expire(key, 7, TimeUnit.DAYS); } /** * 获取实时票数排名 */ public ListVoteRanking getRealTimeRanking(Long topicId) { String key getVoteCountKey(topicId); MapObject, Object voteCounts redisTemplate.opsForHash().entries(key); return voteCounts.entrySet().stream() .map(entry - new VoteRanking( Long.parseLong(entry.getKey().toString()), Long.parseLong(entry.getValue().toString()) )) .sorted((r1, r2) - Long.compare(r2.getCount(), r1.getCount())) .collect(Collectors.toList()); } private String getVoteCountKey(Long topicId) { return vote_count:topic: topicId; } } // 票数排名DTO Data AllArgsConstructor class VoteRanking { private Long optionId; private Long count; }6.2 冠亚军计算算法Component public class ChampionCalculator { /** * 计算冠亚军支持并列情况 */ public ChampionResult calculateChampions(ListVoteRanking rankings) { if (rankings.isEmpty()) { return new ChampionResult(Collections.emptyList(), Collections.emptyList()); } // 按票数分组 MapLong, ListLong countToOptions rankings.stream() .collect(Collectors.groupingBy( VoteRanking::getCount, Collectors.mapping(VoteRanking::getOptionId, Collectors.toList()) )); // 按票数降序排序 ListLong sortedCounts countToOptions.keySet().stream() .sorted(Comparator.reverseOrder()) .collect(Collectors.toList()); ListLong champions new ArrayList(); ListLong runnersUp new ArrayList(); if (!sortedCounts.isEmpty()) { // 冠军第一名 Long championCount sortedCounts.get(0); champions.addAll(countToOptions.get(championCount)); // 亚军第二名 if (sortedCounts.size() 1) { Long runnerUpCount sortedCounts.get(1); runnersUp.addAll(countToOptions.get(runnerUpCount)); } } return new ChampionResult(champions, runnersUp); } }7. API接口设计与实现7.1 控制器层实现RestController RequestMapping(/api/vote) Validated public class VoteController { Autowired private VoteService voteService; Autowired private VoteQueryService voteQueryService; /** * 提交投票 */ PostMapping(/submit) public ResponseEntityApiResponseVoteResult submitVote( Valid RequestBody VoteRequest request, HttpServletRequest httpRequest) { // 补充请求信息IP、User-Agent等 request.setIpAddress(getClientIpAddress(httpRequest)); request.setUserAgent(httpRequest.getHeader(User-Agent)); VoteResult result voteService.vote(request); return ResponseEntity.ok(ApiResponse.success(result)); } /** * 查询投票结果 */ GetMapping(/result/{topicId}) public ResponseEntityApiResponseVoteResultResponse getVoteResult( PathVariable Long topicId) { VoteResultResponse result voteQueryService.getVoteResult(topicId); return ResponseEntity.ok(ApiResponse.success(result)); } /** * 实时排名查询 */ GetMapping(/ranking/{topicId}) public ResponseEntityApiResponseListVoteRanking getRealTimeRanking( PathVariable Long topicId) { ListVoteRanking rankings voteQueryService.getRealTimeRanking(topicId); return ResponseEntity.ok(ApiResponse.success(rankings)); } private String getClientIpAddress(HttpServletRequest request) { // 获取真实IP地址考虑代理情况 String xForwardedFor request.getHeader(X-Forwarded-For); if (StringUtils.hasText(xForwardedFor)) { return xForwardedFor.split(,)[0].trim(); } return request.getRemoteAddr(); } }7.2 请求响应模型// 投票请求 Data public class VoteRequest { NotNull(message 投票主题ID不能为空) private Long topicId; NotNull(message 投票选项ID不能为空) private Long optionId; NotBlank(message 用户标识不能为空) private String userId; private String ipAddress; private String userAgent; } // 统一API响应 Data AllArgsConstructor public class ApiResponseT { private boolean success; private String message; private T data; private Long timestamp; public static T ApiResponseT success(T data) { return new ApiResponse(true, 成功, data, System.currentTimeMillis()); } public static T ApiResponseT error(String message) { return new ApiResponse(false, message, null, System.currentTimeMillis()); } }8. 系统测试与验证8.1 单元测试编写SpringBootTest class VoteServiceTest { Autowired private VoteService voteService; Autowired private VoteTopicRepository voteTopicRepository; Test void testVoteSuccess() { // 准备测试数据 VoteTopic topic createTestVoteTopic(); VoteRequest request new VoteRequest(); request.setTopicId(topic.getId()); request.setOptionId(1L); request.setUserId(test_user_001); // 执行投票 VoteResult result voteService.vote(request); // 验证结果 assertNotNull(result); assertTrue(result.isSuccess()); assertEquals(1, result.getCurrentVoteCount()); } Test void testDuplicateVotePrevention() { VoteTopic topic createTestVoteTopic(); VoteRequest request new VoteRequest(); request.setTopicId(topic.getId()); request.setOptionId(1L); request.setUserId(test_user_002); // 第一次投票应该成功 voteService.vote(request); // 第二次投票应该失败 assertThrows(VoteException.class, () - voteService.vote(request)); } }8.2 性能压力测试Test void testConcurrentVoting() throws InterruptedException { int threadCount 100; CountDownLatch latch new CountDownLatch(threadCount); AtomicInteger successCount new AtomicInteger(0); for (int i 0; i threadCount; i) { new Thread(() - { try { VoteRequest request createConcurrentTestRequest(); voteService.vote(request); successCount.incrementAndGet(); } catch (Exception e) { // 预期部分请求会因锁竞争失败 } finally { latch.countDown(); } }).start(); } latch.await(10, TimeUnit.SECONDS); // 验证数据一致性 Long actualVoteCount voteRecordRepository.countByTopicId(testTopicId); assertEquals(successCount.get(), actualVoteCount); }9. 部署与运维最佳实践9.1 Docker容器化部署# Dockerfile FROM openjdk:17-jdk-slim # 安装必要的工具 RUN apt-get update apt-get install -y curl # 创建应用目录 WORKDIR /app # 复制JAR文件 COPY target/vote-system-1.0.0.jar app.jar # 创建非root用户 RUN groupadd -r spring useradd -r -g spring spring USER spring # 暴露端口 EXPOSE 8080 # 健康检查 HEALTHCHECK --interval30s --timeout3s \ CMD curl -f http://localhost:8080/actuator/health || exit 1 # 启动命令 ENTRYPOINT [java, -jar, app.jar]9.2 生产环境配置建议# application-prod.yml spring: datasource: url: jdbc:mysql://mysql-cluster:3306/vote_system hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 redis: cluster: nodes: - redis-node-1:6379 - redis-node-2:6379 - redis-node-3:6379 password: ${REDIS_PASSWORD} timeout: 5000 # 监控配置 management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always # 日志配置 logging: level: com.example.vote: INFO file: name: /logs/vote-system.log10. 常见问题排查与优化建议10.1 性能问题排查清单问题现象可能原因排查方法解决方案投票响应慢Redis连接池耗尽监控Redis连接数调整连接池大小数据库锁等待并发写入冲突查看数据库锁状态优化事务范围内存使用过高缓存数据过多监控Redis内存设置合适的过期时间10.2 数据一致性保障措施读写分离策略Configuration public class DataSourceConfig { Bean Primary ConfigurationProperties(spring.datasource.master) public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } Bean ConfigurationProperties(spring.datasource.slave) public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } }最终一致性方案Component public class VoteResultCalculator { /** * 定时计算最终结果补偿机制 */ Scheduled(fixedRate 300000) // 5分钟执行一次 public void calculateFinalResult() { // 从Redis获取实时数据 // 与数据库核对 // 修复不一致的数据 } }通过本文的完整实现我们构建了一个高可用、高并发的冠亚投票系统。这个系统不仅解决了传统投票的数据一致性问题还提供了完善的防刷票机制和实时排名功能。在实际项目中你可以根据具体需求调整配置参数比如投票限制规则、缓存策略等。关键要记住的是投票系统的核心在于平衡性能和数据一致性。在分布式环境下合理的锁策略和缓存设计是保证系统稳定运行的基础。建议在正式上线前充分进行压力测试和异常场景测试确保系统在各种边界情况下都能正常工作。

相关新闻

Word转PDF终极指南:格式保真、字体嵌入与文件压缩实战

Word转PDF终极指南:格式保真、字体嵌入与文件压缩实战

1. 项目概述:为什么“Word转PDF”值得深究?在日常办公和文档处理中,把Word文档转换成PDF格式,大概是每个人都会遇到的操作。表面上看,这只是一个简单的“另存为”或“导出”动作,但如果你真的深入去用&…

2026/10/12 16:34:01 阅读更多 →
乐高漫威抽抽乐阿加莎女巫识别技巧与实战指南

乐高漫威抽抽乐阿加莎女巫识别技巧与实战指南

如果你最近在关注乐高漫威系列,特别是抽抽乐(Collectible Minifigures)产品线,那么"阿加莎女巫"这个角色一定不会陌生。作为漫威宇宙中近期人气飙升的反派角色,阿加莎哈克尼斯在《旺达幻视》中的表现让她成为…

2026/10/8 22:10:59 阅读更多 →
三分投篮训练:从动作标准化到实战应用的科学提升路径

三分投篮训练:从动作标准化到实战应用的科学提升路径

如果你有机会跟着斯蒂芬库里——这位NBA历史上最伟大的三分射手——训练一个暑假,你的三分球水平会发生怎样的变化?更重要的是,这种变化能否让你在野球场、单位比赛或者朋友圈的篮球局中脱颖而出,甚至"打爆"身边的对手&…

2026/10/5 21:33:48 阅读更多 →

最新新闻

Qt MinGW 编译 PCL 全家桶:Boost/Eigen/FLANN/Qhull/VTK 完整指南

Qt MinGW 编译 PCL 全家桶:Boost/Eigen/FLANN/Qhull/VTK 完整指南

简介:本资源面向需要在Windows平台使用Qt与MinGW工具链构建三维点云应用的C开发者,提供PCL点云库及其全套依赖的编译成果。PCL专注三维点云采集、预处理、特征提取与表面重建,Qt负责跨平台图形界面,MinGW提供原生Windows编译工具链…

2026/10/13 21:31:26 阅读更多 →
数据仓库实践第一课:吃透数据库基础与SQL底层功

数据仓库实践第一课:吃透数据库基础与SQL底层功

简介:这是一份面向数据仓库初学者与相关从业者的PPT教学课件,聚焦数据库基本概念、关系模型、关系代数与SQL基础练习,帮助学习者从零建立数据管理思维,为后续数据仓库实践打基础。内容涵盖数据、数据库、数据库系统与DBMS的定义&a…

2026/10/13 21:31:26 阅读更多 →
Dify代码执行节点实战:从沙箱原理到工程化应用

Dify代码执行节点实战:从沙箱原理到工程化应用

开头想说说我为什么会对“Dify 集成”里的代码执行节点这么上心。我早期用 Dify 搭工作流,总觉得可视化节点有边界——只能做判断、提取、合并,一旦牵扯到循环处理、数据整形、加密签名这类需求,光靠拖拽就卡住了。后来我把代码执行集成吃透之…

2026/10/13 21:31:26 阅读更多 →
从词法分析到三地址码:手写Pascal子集编译器核心实现全解析

从词法分析到三地址码:手写Pascal子集编译器核心实现全解析

简介:这是一份面向编译原理课程设计的小型Pascal子集编译器设计报告,适合计算机专业学生、课程设计小组及自学编译器基础的读者参考。资源以单个doc文档形式打包,共1个文件,压缩包约952KB,内容涵盖词法分析、语法分析、…

2026/10/13 21:31:26 阅读更多 →
JCache CacheLoader:load与loadAll如何配合getAll实现批量回源?

JCache CacheLoader:load与loadAll如何配合getAll实现批量回源?

几年前的面试,大家喜欢问“缓存穿透怎么解决”“Redis雪崩怎么处理”,这两年风向明显变了,越来越多的团队开始抠规范本身,比如今天要聊的这道题:JCache(JSR-107)中 loadAll 和 load 的用途是什么…

2026/10/13 21:31:26 阅读更多 →
PCL点云圆柱体分割实战:RANSAC原理、参数调优与工业应用

PCL点云圆柱体分割实战:RANSAC原理、参数调优与工业应用

做点云处理这几年,我有个特别深的体会:几乎每个项目里需要找的几何元素,翻来覆去就那么几种——平面、球、圆柱。平面最简单,一个法线加一个距离就描述完;球也不难,中心加半径一共四个参数;唯独…

2026/10/13 21:30:25 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →