Spring Boot用户状态检测系统:睡眠状态识别与智能提醒实战
最近在开发一个智能提醒系统时遇到了一个有趣的技术需求如何准确识别用户状态并触发相应的提醒逻辑。特别是在睡眠状态检测这个场景下传统的方案往往依赖硬件传感器或复杂的生物特征分析但在某些轻量级应用中我们完全可以通过软件层面的状态管理来实现类似功能。本文将围绕状态检测与提醒系统的技术实现分享一套完整的解决方案涵盖状态建模、事件触发、消息推送等核心环节适合移动端和后端开发者参考使用。1. 状态检测的技术背景与应用场景1.1 什么是用户状态检测用户状态检测是指通过软件系统识别用户当前的活动状态如在线、离线、忙碌、空闲、睡眠等。在即时通讯、智能提醒、健康管理等应用中准确的状态判断能够显著提升用户体验。与硬件检测方案相比纯软件方案的优势在于部署成本低、兼容性好但需要更精细的数据建模和算法设计。1.2 典型应用场景分析在实际项目中状态检测技术可以应用于多个场景智能消息推送根据用户状态调整推送策略避免在睡眠时段打扰用户自动化工作流结合状态信息触发相应的业务流程如勿扰模式下的自动回复健康管理通过分析用户作息规律提供个性化的健康建议社交应用显示好友的实时状态增强社交互动的时效性2. 技术选型与环境准备2.1 核心技术与框架实现状态检测系统需要以下技术组件数据采集层移动端SDKAndroid/iOS或Web API用于收集用户行为数据数据处理层使用Spring Boot构建后端服务负责状态计算和逻辑判断消息推送集成第三方推送服务如极光推送、个推或自建WebSocket连接数据存储MySQL用于存储用户配置Redis用于缓存实时状态2.2 开发环境配置示例环境配置如下# Java环境 java version 11.0.12 Spring Boot 2.7.0 # 数据库 MySQL 8.0.28 Redis 6.2.6 # 构建工具 Maven 3.8.12.3 项目结构规划建议采用标准的分层架构src/ ├── main/ │ ├── java/ │ │ └── com/example/status/ │ │ ├── controller/ # 控制层 │ │ ├── service/ # 业务层 │ │ ├── repository/ # 数据层 │ │ ├── model/ # 实体类 │ │ └── config/ # 配置类 │ └── resources/ │ ├── application.yml # 主配置文件 │ └── static/ # 静态资源3. 状态建模与核心算法3.1 用户状态数据模型设计首先定义用户状态的核心实体类// 文件路径src/main/java/com/example/status/model/UserStatus.java public class UserStatus { private Long userId; private StatusType currentStatus; // 当前状态 private LocalDateTime lastUpdateTime; // 最后更新时间 private String timezone; // 用户时区 private SleepSchedule sleepSchedule; // 睡眠计划配置 // 枚举定义状态类型 public enum StatusType { ONLINE, // 在线 OFFLINE, // 离线 BUSY, // 忙碌 SLEEPING, // 睡眠中 AWAY // 离开 } // 睡眠计划配置类 public static class SleepSchedule { private LocalTime usualBedtime; // 通常就寝时间 private LocalTime usualWakeupTime; // 通常起床时间 private boolean autoDetection; // 是否启用自动检测 } }3.2 状态判断算法实现基于时间和用户行为的复合判断逻辑// 文件路径src/main/java/com/example/status/service/StatusDetectionService.java Service public class StatusDetectionService { Autowired private UserStatusRepository statusRepository; public UserStatus detectCurrentStatus(Long userId) { UserStatus userStatus statusRepository.findByUserId(userId); if (userStatus null) { return createDefaultStatus(userId); } // 多重条件判断 if (isSleepingBySchedule(userStatus)) { userStatus.setCurrentStatus(UserStatus.StatusType.SLEEPING); } else if (isSleepingByActivity(userStatus)) { userStatus.setCurrentStatus(UserStatus.StatusType.SLEEPING); } else { userStatus.setCurrentStatus(detectOtherStatus(userStatus)); } userStatus.setLastUpdateTime(LocalDateTime.now()); return statusRepository.save(userStatus); } private boolean isSleepingBySchedule(UserStatus status) { LocalTime now LocalTime.now(); LocalTime bedtime status.getSleepSchedule().getUsualBedtime(); LocalTime wakeupTime status.getSleepSchedule().getUsualWakeupTime(); // 处理跨夜时间判断 if (bedtime.isAfter(wakeupTime)) { return now.isAfter(bedtime) || now.isBefore(wakeupTime); } else { return now.isAfter(bedtime) now.isBefore(wakeupTime); } } private boolean isSleepingByActivity(UserStatus status) { // 基于用户最后活动时间判断 // 如果超过一定时间无活动可能处于睡眠状态 Duration inactiveDuration Duration.between( status.getLastUpdateTime(), LocalDateTime.now()); return inactiveDuration.toHours() 6; // 6小时无活动视为睡眠 } }4. 完整实战案例睡眠状态检测系统4.1 数据库表结构设计创建用户状态管理相关的数据表-- 用户状态表 CREATE TABLE user_status ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL UNIQUE, current_status VARCHAR(20) NOT NULL DEFAULT OFFLINE, last_update_time DATETIME NOT NULL, timezone VARCHAR(50) DEFAULT Asia/Shanghai, usual_bedtime TIME, usual_wakeup_time TIME, auto_detection BOOLEAN DEFAULT true, created_time DATETIME DEFAULT CURRENT_TIMESTAMP, updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 用户活动记录表 CREATE TABLE user_activity ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, activity_type VARCHAR(30) NOT NULL, activity_time DATETIME NOT NULL, device_info VARCHAR(200), INDEX idx_user_time (user_id, activity_time) );4.2 Spring Boot 核心配置应用配置文件示例# 文件路径src/main/resources/application.yml spring: datasource: url: jdbc:mysql://localhost:3306/status_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 password: database: 0 jpa: hibernate: ddl-auto: update show-sql: true app: status: detection: sleep-threshold-hours: 6 check-interval-minutes: 54.3 状态检测定时任务实现定时状态检测和更新// 文件路径src/main/java/com/example/status/scheduler/StatusUpdateScheduler.java Component Slf4j public class StatusUpdateScheduler { Autowired private StatusDetectionService statusDetectionService; Autowired private UserStatusRepository userStatusRepository; Scheduled(fixedRate 300000) // 每5分钟执行一次 public void autoUpdateUserStatus() { log.info(开始自动更新用户状态); ListLong activeUserIds userStatusRepository.findActiveUserIds(); for (Long userId : activeUserIds) { try { UserStatus updatedStatus statusDetectionService.detectCurrentStatus(userId); log.debug(用户 {} 状态更新为: {}, userId, updatedStatus.getCurrentStatus()); } catch (Exception e) { log.error(更新用户 {} 状态失败: {}, userId, e.getMessage()); } } } }4.4 RESTful API 接口实现提供状态查询和管理的API接口// 文件路径src/main/java/com/example/status/controller/StatusController.java RestController RequestMapping(/api/status) Validated public class StatusController { Autowired private StatusDetectionService statusDetectionService; GetMapping(/{userId}) public ResponseEntityUserStatus getCurrentStatus(PathVariable Long userId) { UserStatus status statusDetectionService.detectCurrentStatus(userId); return ResponseEntity.ok(status); } PostMapping(/{userId}/sleep) public ResponseEntityString setSleepStatus(PathVariable Long userId) { UserStatus status statusDetectionService.detectCurrentStatus(userId); status.setCurrentStatus(UserStatus.StatusType.SLEEPING); status.setLastUpdateTime(LocalDateTime.now()); // 保存更新 statusDetectionService.updateStatus(status); return ResponseEntity.ok(睡眠状态设置成功); } PutMapping(/{userId}/schedule) public ResponseEntityString updateSleepSchedule( PathVariable Long userId, RequestBody SleepScheduleRequest request) { // 更新用户睡眠计划 statusDetectionService.updateSleepSchedule(userId, request); return ResponseEntity.ok(睡眠计划更新成功); } }4.5 前端状态展示组件简单的Vue.js组件示例展示如何显示用户状态!-- 文件路径src/main/resources/static/js/status-display.vue -- template div classstatus-container div classstatus-indicator :classstatusClass {{ statusText }} /div div classlast-update 最后更新: {{ lastUpdateTime }} /div /div /template script export default { props: { userId: { type: Number, required: true } }, data() { return { currentStatus: null, lastUpdateTime: null } }, computed: { statusClass() { return { status-online: this.currentStatus ONLINE, status-sleeping: this.currentStatus SLEEPING, status-busy: this.currentStatus BUSY } }, statusText() { const statusMap { ONLINE: 在线, SLEEPING: 睡眠中, BUSY: 忙碌, OFFLINE: 离线 } return statusMap[this.currentStatus] || 未知状态 } }, mounted() { this.fetchStatus() setInterval(this.fetchStatus, 300000) // 每5分钟更新一次 }, methods: { async fetchStatus() { try { const response await fetch(/api/status/${this.userId}) const data await response.json() this.currentStatus data.currentStatus this.lastUpdateTime this.formatTime(data.lastUpdateTime) } catch (error) { console.error(获取状态失败:, error) } }, formatTime(timestamp) { return new Date(timestamp).toLocaleString(zh-CN) } } } /script style .status-container { padding: 10px; border-radius: 5px; background: #f5f5f5; } .status-indicator { display: inline-block; padding: 4px 8px; border-radius: 3px; color: white; font-size: 12px; } .status-online { background: #52c41a; } .status-sleeping { background: #faad14; } .status-busy { background: #f5222d; } /style5. 常见问题与解决方案5.1 状态判断准确性问题问题现象系统错误地将活跃用户判断为睡眠状态可能原因时区配置错误导致时间判断偏差用户活动数据采集不完整睡眠阈值设置不合理解决方案// 增强时区处理逻辑 public class TimeZoneUtils { public static LocalDateTime convertToUserTimezone(LocalDateTime serverTime, String userTimezone) { ZoneId serverZone ZoneId.systemDefault(); ZoneId userZone ZoneId.of(userTimezone); return serverTime.atZone(serverZone) .withZoneSameInstant(userZone) .toLocalDateTime(); } } // 优化活动检测逻辑 public class EnhancedActivityDetector { public boolean hasRecentActivity(Long userId, Duration maxInactiveDuration) { // 检查多种活动类型登录、消息发送、页面浏览等 ListActivityType relevantActivities Arrays.asList( ActivityType.LOGIN, ActivityType.MESSAGE_SEND, ActivityType.PAGE_VIEW ); return activityRepository.existsRecentActivity( userId, relevantActivities, maxInactiveDuration); } }5.2 性能优化策略问题现象用户量增大后系统响应变慢优化方案使用Redis缓存热点用户状态实现状态变化的增量更新采用分库分表策略// Redis缓存实现 Component public class StatusCacheService { Autowired private RedisTemplateString, Object redisTemplate; private static final String STATUS_KEY_PREFIX user:status:; private static final Duration CACHE_TTL Duration.ofMinutes(10); public void cacheUserStatus(UserStatus status) { String key STATUS_KEY_PREFIX status.getUserId(); redisTemplate.opsForValue().set(key, status, CACHE_TTL); } public UserStatus getCachedStatus(Long userId) { String key STATUS_KEY_PREFIX userId; return (UserStatus) redisTemplate.opsForValue().get(key); } }6. 最佳实践与工程建议6.1 数据一致性与事务管理在状态更新过程中确保数据一致性Service Transactional public class StatusManagementService { public void updateStatusWithTransaction(Long userId, StatusType newStatus) { try { // 1. 更新数据库状态 UserStatus status statusRepository.findByUserId(userId); status.setCurrentStatus(newStatus); status.setLastUpdateTime(LocalDateTime.now()); statusRepository.save(status); // 2. 更新缓存 statusCacheService.cacheUserStatus(status); // 3. 发送状态变更事件 eventPublisher.publishEvent(new StatusChangeEvent(this, userId, newStatus)); } catch (Exception e) { // 事务回滚确保数据一致性 throw new RuntimeException(状态更新失败, e); } } }6.2 监控与日志记录建立完善的监控体系Aspect Component Slf4j public class StatusMonitoringAspect { Around(execution(* com.example.status.service.*.*(..))) public Object monitorServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable { String methodName joinPoint.getSignature().getName(); long startTime System.currentTimeMillis(); try { Object result joinPoint.proceed(); long executionTime System.currentTimeMillis() - startTime; log.info(方法 {} 执行成功耗时: {}ms, methodName, executionTime); // 记录指标到监控系统 Metrics.counter(status_service_method, method, methodName) .increment(); Metrics.timer(status_service_duration, method, methodName) .record(executionTime, TimeUnit.MILLISECONDS); return result; } catch (Exception e) { log.error(方法 {} 执行失败: {}, methodName, e.getMessage()); Metrics.counter(status_service_error, method, methodName) .increment(); throw e; } } }6.3 安全与权限控制确保状态信息的安全访问Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/api/status/**).authenticated() .antMatchers(/api/admin/**).hasRole(ADMIN) .anyRequest().permitAll() .and() .oauth2ResourceServer().jwt(); } } // 方法级权限控制 Service public class SecureStatusService { PreAuthorize(#userId authentication.principal.id or hasRole(ADMIN)) public UserStatus getStatusSecurely(Long userId) { return statusDetectionService.detectCurrentStatus(userId); } }7. 扩展功能与进阶优化7.1 机器学习增强的状态预测基于历史数据训练预测模型# 文件路径scripts/status_predictor.py import pandas as pd from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split class StatusPredictor: def __init__(self): self.model RandomForestClassifier(n_estimators100) def train(self, historical_data): # 特征工程时间特征、行为模式、历史状态等 features self.extract_features(historical_data) labels historical_data[status] X_train, X_test, y_train, y_test train_test_split( features, labels, test_size0.2) self.model.fit(X_train, y_train) accuracy self.model.score(X_test, y_test) print(f模型准确率: {accuracy:.2f}) def predict_next_status(self, user_data): features self.extract_features([user_data]) prediction self.model.predict(features) return prediction[0]7.2 多维度状态融合分析结合多个数据源提高判断准确性public class MultiSourceStatusDetector { public StatusType detectWithMultipleSources(Long userId) { // 1. 基于时间规律的分析 TimeBasedAnalysis timeAnalysis analyzeTimePattern(userId); // 2. 基于行为模式的分析 BehaviorAnalysis behaviorAnalysis analyzeBehaviorPattern(userId); // 3. 基于设备传感器的分析如果可用 SensorDataAnalysis sensorAnalysis analyzeSensorData(userId); // 4. 加权融合决策 return fuseDecisions(timeAnalysis, behaviorAnalysis, sensorAnalysis); } private StatusType fuseDecisions(AnalysisResult... analyses) { MapStatusType, Double scores new HashMap(); for (AnalysisResult analysis : analyses) { for (Map.EntryStatusType, Double entry : analysis.getConfidenceScores().entrySet()) { scores.merge(entry.getKey(), entry.getValue(), Double::sum); } } return scores.entrySet().stream() .max(Map.Entry.comparingByValue()) .map(Map.Entry::getKey) .orElse(StatusType.OFFLINE); } }本文详细介绍了用户状态检测系统的完整实现方案从基础概念到实战代码涵盖了系统设计的各个环节。在实际项目中建议根据具体业务需求调整判断逻辑和参数设置特别是在处理用户隐私数据时要严格遵守相关法律法规。通过合理的架构设计和持续的优化迭代可以构建出既准确又高效的智能状态管理系统。

相关新闻

Java中文乱码全解析:从字符编码原理到实战解决方案

Java中文乱码全解析:从字符编码原理到实战解决方案

1. 从“锟斤拷”说起:为什么中文乱码是Java开发者的必修课如果你在Java开发中没见过“锟斤拷”或者“烫烫烫”,那你的职业生涯可能还不够完整。这当然是个玩笑,但背后反映的是一个严肃且普遍的问题:中文乱码。它就像一个幽灵&…

2026/10/13 14:18:18 阅读更多 →
Python数据分析实战:从数据清洗到可视化呈现的完整行业盈利分析流程

Python数据分析实战:从数据清洗到可视化呈现的完整行业盈利分析流程

1. 项目缘起:为什么我们需要一个“闯关式”的行业盈利分析实验?最近在带团队做数据分析培训,发现一个挺普遍的问题:很多朋友学了Python、Pandas、Matplotlib,看教程时感觉都懂了,一到自己动手分析真实的行业…

2026/10/13 0:15:33 阅读更多 →
verilog HDLBits刷题[Finite State Machines]“Lemmings2”---Lemmings2

verilog HDLBits刷题[Finite State Machines]“Lemmings2”---Lemmings2

1、题目 See also: Lemmings1. In addition to walking left and right, Lemmings will fall (and presumably go "aaah!") if the ground disappears underneath them. In addition to walking left and right and changing direction when bumped, when ground0…

2026/10/9 10:55:18 阅读更多 →

最新新闻

虚拟机+Linux入门:从安装配置到快照恢复的完整指南

虚拟机+Linux入门:从安装配置到快照恢复的完整指南

直接学Linux的人,十有八九第一堂课都是在装虚拟机。我自己当年学Linux,一开始直接拿一台老笔记本去装系统,结果分区的时候手一抖,把资料盘给格了,那种酸爽至今记忆犹新。后来老老实实按部就班用虚拟机,才发…

2026/10/13 16:54:42 阅读更多 →
SpringBoot+Vue3+MyBatis乡村政务办公系统开发与部署全解析

SpringBoot+Vue3+MyBatis乡村政务办公系统开发与部署全解析

身边不少朋友做过政务类的管理系统,我经手的项目里也有不少类似的诉求。乡村政务办公系统这类项目,看起来就是典型的CRUD加权限管理,但真正落地时,细节远比想象中多。尤其是面对乡、村两级的工作人员,系统不光要功能完…

2026/10/13 16:54:42 阅读更多 →
t3code:轻量级代码片段管理方案,纯文本与可检索的极简实践

t3code:轻量级代码片段管理方案,纯文本与可检索的极简实践

1. 项目缘起与核心定位第一次看到“t3code”这个名字,我下意识以为是某个新出的终端工具或者代码片段管理器。翻了一圈社区讨论和零散的项目描述之后才反应过来,它其实是一个面向轻量级代码片段与结构化文本的极简组织方案——你可以把它理解成“给开发者…

2026/10/13 16:54:42 阅读更多 →
用 CLI 终结团队代码规范之争:t3code 的设计与落地

用 CLI 终结团队代码规范之争:t3code 的设计与落地

大概半年前,我们团队在 Code Review 时的画风是这样的:有人坚持单引号,有人只用双引号;有人习惯把工具函数丢进utils.ts,有人喜欢按业务模块建helpers文件夹;新同事入职第一周,光适应"潜规…

2026/10/13 16:54:42 阅读更多 →
用Python打造Mercari新品扫描器:轻量爬虫与增量去重实战

用Python打造Mercari新品扫描器:轻量爬虫与增量去重实战

简介:mercari-scanner 是一套基于 Python 的 Mercari 新商品扫描与提醒工具,面向海淘代购人群和二手市场研究者,旨在解决手动刷新页面容易错过心仪商品的问题。工具支持通过命令行参数 --min-price 与 --max-price 设置价格区间,并…

2026/10/13 16:54:42 阅读更多 →
AI能力图谱日报:从热词到工程落地的决策指南

AI能力图谱日报:从热词到工程落地的决策指南

1. 这份“AI 日报”不是新闻简报,而是一份动态能力图谱很多人看到“AI 日报(2026年10月6日)”这个标题,第一反应是点开看今天又出了什么大模型、哪家公司发了新论文、又有哪个开源项目爆火了——就像刷微博热搜那样扫一眼就划走。…

2026/10/13 16:53:41 阅读更多 →

日新闻

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

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

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

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 阅读更多 →