局域网考试系统实战:断网环境下的高并发交卷与断电恢复方案
简介这是一套面向教学评估与考试组织场景的局域网考试系统完整源码包适合高校教师、教务管理人员及.NET开发者研究部署或二次定制。系统采用B/S架构覆盖试题库管理、组卷策略、考生信息维护、自动批阅、防作弊与成绩统计等核心模块可在本地网络中完成考试预约、答题与即时评分全流程。压缩包共84个文件约4.08MB以cs源码、resx资源文件、exe可执行程序、msi安装包及ini配置为主另含sln解决方案、mdb数据库与少量图片素材服务端与客户端工程结构清晰便于按模块阅读与调试。目前已有456人学习下载。借助这份含源码的资源读者可快速理解考试系统的整体设计思路掌握组卷算法、成绩管理与安全机制的实现方式并基于现有工程进行功能拓展或教学演示是学习.NET桌面与Web混合开发的实用参考案例。1. 局域网考试系统断网也能跑为什么越来越多考场选它断网、禁外设、屏蔽信号这是很多标准化考场的常规操作。可监考老师手里那台电脑还得实时收卷、判分、统计成绩——网络断了系统怎么活答案就是局域网考试系统。它把服务端和客户端部署在同一网段内不依赖公网考生机通过交换机直连服务器交卷、判分、成绩汇总全在本地闭环完成。适合学校机房、企业内训考核、认证考试中心这类对网络隔离有硬要求的场景。我做过几套类似的部署最大的感受是这东西的难点不在功能多花哨而在断网、断电、高并发交卷这三件事上能不能稳住。下面把我踩过的坑和能直接抄的配置讲清楚。2. 局域网考试系统的架构选型C/S 还是 B/S先想清楚这三件事2.1 两种架构的真实差异不在界面在部署和容灾很多人一上来就问 C/S 和 B/S 哪个好其实这个问题问偏了。局域网考试系统的核心约束是「服务端必须在内网可达」至于客户端是浏览器还是桌面程序取决于你的考场环境。B/S 架构的优势是零安装考生机只要有浏览器输入服务器 IP 就能进考试页。机房机器多、系统版本杂的时候这个优势非常明显。但 B/S 的短板也突出——浏览器兼容性、页面刷新丢答案、断网后 WebSocket 重连这些都是高频翻车点。C/S 架构需要每台考生机装客户端部署工作量大但客户端可以本地缓存答题记录断网瞬间不丢数据恢复后自动续传。如果你的考场对交卷可靠性要求极高C/S 更稳。我的建议是机房统一镜像、能批量装软件的走 C/S机器杂、临时考场、不想装东西的走 B/S但必须做本地缓存兜底。2.2 服务端选型的三个硬指标不管前端选什么服务端要盯住三个指标指标最低要求推荐值说明并发连接数考生数 × 1.5考生数 × 3留出重连和心跳冗余交卷写入延迟 500ms 100ms集中交卷时不能排队断电恢复时间 30s 10s服务重启后数据不丢并发连接数为什么要乘系数因为考生机在考试过程中会不断重连心跳超时、页面刷新、网络抖动实际连接数远大于考生人数。我见过 60 人的考场服务端只配了 80 个连接上限结果开考 10 分钟就有人连不上排查半天才发现是连接池满了。交卷写入延迟是另一个容易被忽略的点。如果每份答卷都走数据库事务集中交卷时数据库会成为瓶颈。常见做法是交卷先写内存队列或本地文件再由后台线程批量落库。2.3 数据存储为什么我最终选了 SQLite 文件双写很多教程一上来就推荐 MySQL但在局域网考试系统这个场景里SQLite 往往更合适。原因很简单考场环境通常只有一台服务器不需要分布式SQLite 零配置、单文件、断电恢复能力强。但纯 SQLite 也有风险——如果服务器硬盘故障数据全丢。所以我的做法是双写答题过程写 SQLite交卷时同时生成一份 JSON 文件存到另一个目录。这样即使数据库损坏也能从 JSON 恢复。import sqlite3 import json import os from datetime import datetime DB_PATH /data/exam/exam.db BACKUP_DIR /data/exam/backup def init_db(): 初始化数据库创建答题记录表 conn sqlite3.connect(DB_PATH) conn.execute( CREATE TABLE IF NOT EXISTS answers ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id TEXT NOT NULL, question_id TEXT NOT NULL, answer TEXT, submit_time TEXT, status TEXT DEFAULT draft ) ) conn.execute(CREATE INDEX IF NOT EXISTS idx_student ON answers(student_id)) conn.commit() return conn def save_answer(conn, student_id, question_id, answer): 保存单题答案status 为 draft 表示暂存 conn.execute( INSERT OR REPLACE INTO answers (student_id, question_id, answer, submit_time, status) VALUES (?, ?, ?, ?, draft), (student_id, question_id, answer, datetime.now().isoformat()) ) conn.commit() def submit_paper(conn, student_id): 交卷更新状态并写 JSON 备份 conn.execute( UPDATE answers SET statussubmitted WHERE student_id? AND statusdraft, (student_id,) ) conn.commit() # 查出该考生所有答案 rows conn.execute( SELECT question_id, answer FROM answers WHERE student_id? AND statussubmitted, (student_id,) ).fetchall() backup {qid: ans for qid, ans in rows} os.makedirs(BACKUP_DIR, exist_okTrue) path os.path.join(BACKUP_DIR, f{student_id}_{datetime.now().strftime(%Y%m%d%H%M%S)}.json) with open(path, w, encodingutf-8) as f: json.dump(backup, f, ensure_asciiFalse, indent2) return path这段代码的关键点INSERT OR REPLACE保证同一题重复作答时覆盖旧答案不会产生脏数据status字段区分暂存和已交卷避免误判交卷时先更新状态再写 JSON顺序不能反否则 JSON 里可能混入未完成的答案。BACKUP_DIR建议放在不同物理磁盘上如果只有一块盘至少放在不同分区。3. 把服务端跑起来从零到能收卷的最小步骤3.1 环境准备与依赖安装假设你用的是一台干净的 Linux 服务器Ubuntu 20.04 或 CentOS 7 都行先装 Python 3.8 和必要依赖。局域网考试系统不需要公网所以 pip 源可以用内网镜像或者提前把 whl 包拷进去。# 创建专用用户避免用 root 跑服务 sudo useradd -r -s /bin/false examsvc sudo mkdir -p /data/exam/backup sudo chown -R examsvc:examsvc /data/exam # 安装依赖离线环境提前下载 whl pip3 install fastapi uvicorn websockets为什么用 FastAPI 而不是 Flask因为局域网考试系统需要 WebSocket 做实时交卷通知和心跳检测FastAPI 原生支持异步和 WebSocket写起来更顺。uvicorn 作为 ASGI 服务器单进程就能扛几百并发足够中小考场用。3.2 服务端核心接口与 WebSocket 心跳服务端至少要有三个接口获取试卷、暂存答案、交卷。WebSocket 用来做两件事服务端主动通知考生「还有 5 分钟」以及客户端定时发心跳证明自己还在线。from fastapi import FastAPI, WebSocket, WebSocketDisconnect from pydantic import BaseModel import asyncio app FastAPI() conn init_db() class AnswerItem(BaseModel): student_id: str question_id: str answer: str app.post(/api/save) async def save(item: AnswerItem): 暂存单题答案前端每次切题或每 30 秒调用一次 save_answer(conn, item.student_id, item.question_id, item.answer) return {ok: True} app.post(/api/submit/{student_id}) async def submit(student_id: str): 交卷返回备份文件路径 path submit_paper(conn, student_id) return {ok: True, backup: path} app.websocket(/ws/{student_id}) async def ws_endpoint(ws: WebSocket, student_id: str): WebSocket 心跳与通知通道 await ws.accept() try: while True: # 等待客户端消息超时 30 秒视为断线 data await asyncio.wait_for(ws.receive_text(), timeout30) if data ping: await ws.send_text(pong) except asyncio.TimeoutError: # 超时未收到心跳记录断线但不立即踢出 print(f{student_id} heartbeat timeout) except WebSocketDisconnect: print(f{student_id} disconnected)/api/save设计成幂等接口前端可以放心重试。/api/submit的路径参数是学号交卷后返回备份路径方便排查。WebSocket 的 30 秒超时是经验值——太短会误判考生思考时不会一直动鼠标太长则断线发现不及时。实际部署时建议把超时时间做成配置项根据考场网络质量调整。3.3 客户端答题页的本地缓存策略B/S 架构最大的风险是页面刷新或浏览器崩溃导致答案丢失。解决办法是在前端用 localStorage 做本地缓存每次答案变更先写本地再异步发到服务端。// 答题页本地缓存先存本地再发服务端 const CACHE_KEY exam_answers_ studentId; function saveAnswer(questionId, answer) { // 1. 写本地缓存 let cache JSON.parse(localStorage.getItem(CACHE_KEY) || {}); cache[questionId] answer; localStorage.setItem(CACHE_KEY, JSON.stringify(cache)); // 2. 异步发服务端失败不阻塞 fetch(/api/save, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({ student_id: studentId, question_id: questionId, answer: answer }) }).catch(err { console.warn(服务端暂存失败本地已保留, err); }); } // 页面加载时恢复本地缓存 function restoreCache() { const cache JSON.parse(localStorage.getItem(CACHE_KEY) || {}); for (const [qid, ans] of Object.entries(cache)) { const el document.querySelector([data-qid${qid}]); if (el) el.value ans; } }这段代码的核心逻辑是「本地优先」不管服务端通不通答案先落在浏览器里。catch里只打警告不弹窗避免干扰考生。restoreCache在页面加载时调用即使刷新也能恢复。注意 localStorage 有 5MB 上限纯文本答案完全够用但如果试卷里有图片附件需要改用 IndexedDB。4. 高并发交卷与断电恢复三个必须压测的场景4.1 集中交卷时数据库写入排队怎么破考试结束前 5 分钟是交卷高峰如果 60 个人同时点交卷每人 50 道题瞬间就是 3000 次写入。SQLite 默认的串行写入会排队导致部分考生看到「交卷中」转圈很久。解决办法是交卷走队列接口收到交卷请求后只把学号推入内存队列并立即返回「已收到」后台协程逐个处理落库。import asyncio from collections import deque submit_queue deque() queue_lock asyncio.Lock() app.post(/api/submit/{student_id}) async def submit(student_id: str): 交卷请求只入队立即返回 async with queue_lock: submit_queue.append(student_id) return {ok: True, msg: 已收到正在处理} async def submit_worker(): 后台协程逐个处理交卷队列 while True: async with queue_lock: if submit_queue: sid submit_queue.popleft() else: sid None if sid: try: submit_paper(conn, sid) print(f{sid} 交卷完成) except Exception as e: print(f{sid} 交卷失败: {e}) # 失败重新入队最多重试 3 次 async with queue_lock: submit_queue.append(sid) else: await asyncio.sleep(0.1) app.on_event(startup) async def startup(): asyncio.create_task(submit_worker())队列方案的关键是「先响应再处理」考生端不会卡。submit_worker每次只处理一个避免数据库锁竞争。失败重入队保证了可靠性但要注意加最大重试次数否则死循环。实际压测时60 人集中交卷从原来的 12 秒降到 2 秒以内。4.2 服务端断电后如何恢复到断点断电是考场最怕的事。如果服务端没有做持久化重启后所有暂存答案全丢。我的做法是除了 SQLite 的 WAL 模式再加一层「操作日志」。import logging from logging.handlers import RotatingFileHandler # 操作日志每次暂存和交卷都记一行 op_logger logging.getLogger(exam_op) op_logger.setLevel(logging.INFO) handler RotatingFileHandler(/data/exam/op.log, maxBytes10*1024*1024, backupCount5) handler.setFormatter(logging.Formatter(%(asctime)s %(message)s)) op_logger.addHandler(handler) def save_answer_with_log(conn, student_id, question_id, answer): 带操作日志的暂存 save_answer(conn, student_id, question_id, answer) op_logger.info(fSAVE {student_id} {question_id} {answer[:50]})WAL 模式让 SQLite 在写入时不会阻塞读断电后能自动恢复到最后一次检查点。操作日志则是最后一道保险——如果数据库文件损坏可以用日志重放恢复。日志按 10MB 滚动保留 5 个备份足够一场考试用。4.3 压测脚本模拟 60 人同时交卷上线前一定要压测。用 Python 的aiohttp写个简单脚本模拟 60 个考生同时交卷。import aiohttp import asyncio import time async def mock_submit(session, student_id): 模拟一个考生交卷 url fhttp://127.0.0.1:8000/api/submit/{student_id} start time.time() async with session.post(url) as resp: await resp.json() return time.time() - start async def main(): async with aiohttp.ClientSession() as session: tasks [mock_submit(session, fstu_{i:03d}) for i in range(60)] results await asyncio.gather(*tasks) print(f60 人交卷平均耗时 {sum(results)/len(results):.3f}s最大 {max(results):.3f}s) asyncio.run(main())压测时重点看三个数平均耗时、最大耗时、服务端 CPU 和内存。如果最大耗时超过 1 秒说明队列处理不过来需要调大 worker 数量或优化数据库写入。我一般会把submit_worker改成多个协程并行但要注意 SQLite 的写锁并行度不要超过 4。5. 局域网考试系统避坑这 5 个问题我全踩过5.1 考生机 IP 冲突导致部分人连不上现象开考后陆续有考生反馈「无法连接服务器」但同一排的其他机器正常。原因机房 DHCP 分配混乱或者有人手动改了 IP导致地址冲突。解决考前用静态 IP 绑定 MAC或者在服务端加一层连接来源校验发现同一 IP 多个学号登录时告警。更彻底的做法是让考生机通过主机名访问而不是 IP。5.2 浏览器自动更新导致答题页白屏现象考试中途部分考生浏览器自动更新页面刷新后白屏。原因B/S 架构依赖浏览器后台更新会强制重启标签页。解决考前关闭浏览器自动更新或者用 C/S 客户端彻底规避。如果只能用 B/S在页面里加beforeunload提示并且本地缓存必须可靠。5.3 交卷时提示「数据库锁定」现象集中交卷时部分考生看到「database is locked」错误。原因SQLite 默认的写锁是排他的多个写入同时来就会锁。解决开启 WAL 模式并且把交卷写入串行化就是前面说的队列方案。WAL 模式下读不阻塞写但写与写之间仍然互斥所以队列不能省。5.4 服务端时间不准导致倒计时错乱现象考生端倒计时和服务端不一致有人提前被强制交卷。原因服务器没配 NTP时间漂移或者客户端用本地时间算倒计时。解决倒计时以服务端时间为准客户端定期同步。服务端开 NTP考场内网如果有时间服务器就指向它没有就手动校准。5.5 断电后重启部分考生答案丢失现象断电重启后有考生反馈最后几道题答案没了。原因暂存接口是异步的断电时内存里还有未落库的数据。解决暂存接口改成同步写 SQLite虽然慢一点但可靠。或者用操作日志重放把断电前最后几秒的操作补回来。我现在的做法是暂存同步写、交卷走队列兼顾可靠和性能。6. 进阶技巧用快照回滚把故障恢复压到 10 秒内前面讲的都是「怎么不出事」这一章讲「出了事怎么最快恢复」。局域网考试系统最怕的不是故障而是故障后恢复太慢影响考试进度。我的做法是给服务端加一层「快照回滚」每 30 秒把 SQLite 数据库文件复制一份到内存盘tmpfs同时记录当前时间戳。如果服务崩溃重启后先从内存盘恢复最近快照再用操作日志重放快照之后的变更。# 创建内存盘挂载点重启后消失正好符合快照语义 sudo mkdir -p /mnt/exam_snap sudo mount -t tmpfs -o size512M tmpfs /mnt/exam_snap # 快照脚本每 30 秒复制一次数据库 while true; do cp /data/exam/exam.db /mnt/exam_snap/exam_snap.db date %s /mnt/exam_snap/snap_ts sleep 30 done恢复逻辑服务启动时检查/mnt/exam_snap/exam_snap.db是否存在存在则用它覆盖/data/exam/exam.db然后从snap_ts记录的时间点开始重放op.log。这样即使数据库文件损坏最多丢 30 秒数据。这个方案的关键参数是快照间隔。30 秒是权衡结果太短会增加磁盘 IO太长丢数据多。512MB 内存盘对纯文本答案来说绰绰有余一场 60 人的考试数据库通常不到 10MB。还有一个细节快照脚本和服务端要分开进程避免服务崩溃时快照也停了。我用 systemd 把快照脚本做成独立服务Restartalways挂了自动拉起。最后说个血泪教训千万别在考试进行中做数据库迁移或表结构变更。我有一次想优化索引结果锁表 3 分钟全场考生卡住。任何变更都要在考前完成考试期间只读不写结构。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Pygame实战教程:从零开发打砖块游戏,掌握游戏循环与碰撞检测

Pygame实战教程:从零开发打砖块游戏,掌握游戏循环与碰撞检测

1. 为什么我用Pygame做第一个游戏,而不是直接上游戏引擎先交代一个背景:很多人说学编程要有项目练手,但真到了“做点什么”的时候,反而一脑袋浆糊——写爬虫怕被封、做网站要学前端后端一大套、搞数据分析又觉得跟工作生活没关联。…

2026/10/14 3:49:40 阅读更多 →
Gradle依赖统一管理:Version Catalog与BOM实战避坑指南

Gradle依赖统一管理:Version Catalog与BOM实战避坑指南

简介:这份资源聚焦 Android 工程中 Gradle 依赖的统一管理,面向需要规范多模块依赖版本、减少重复配置的 Android 开发者,尤其适合已掌握 Gradle 基础、希望进一步优化构建脚本的中级工程师。压缩包共 34 个文件,约 100KB&#xf…

2026/10/14 3:49:40 阅读更多 →
高校成绩可视化分析平台的设计与实现:从数据清洗到图表呈现

高校成绩可视化分析平台的设计与实现:从数据清洗到图表呈现

前阵子帮某高校的教务部门做了一套成绩统计分析平台,项目名称就叫“基于大数据校园大学生成绩可视化分析平台设计与实现”。听起来是个很重的系统,实际上只要把需求拆开看,就三件事:全量成绩数据入库、多维交叉统计、图表化呈现。…

2026/10/14 3:49:40 阅读更多 →

最新新闻

2025美赛C题:从时序面板到负二项回归的奥运会奖牌预测

2025美赛C题:从时序面板到负二项回归的奥运会奖牌预测

简介:2025年美赛C题建模与数据分析代码合集,面向参赛学生、数据分析爱好者与教练团队,围绕奥运奖牌预测与相关因素分析提供完整可运行的Python解决方案。资源覆盖数据清洗、特征工程、奖牌预测、进步退步分析、零奖牌统计、奖牌突破概率、东道…

2026/10/14 4:36:54 阅读更多 →
粉末冶金压力机三维模型参数化构建:模架设计与工程图随动全流程

粉末冶金压力机三维模型参数化构建:模架设计与工程图随动全流程

粉末冶金压力机及其模架的三维模型构建,最容易被低估的一点,就是它不像画一个零件那么简单——整机、模架、模具、传动、脱模机构全都要塞进同一套数据里,还要保证尺寸改一处、模型和工程图一起动。我们这次做的是12吨级机型,三维…

2026/10/14 4:36:54 阅读更多 →
多孔介质燃烧器COMSOL仿真:多物理场耦合建模与实操指南

多孔介质燃烧器COMSOL仿真:多物理场耦合建模与实操指南

做多孔介质燃烧器仿真这件事,绕不开COMSOL这个工具。我手头这个多物理场模型,就把层流流动、流体传热、浓物质传递和化学反应四个模块全塞进了同一个多孔介质结构里,目标是把“燃烧器内部到底发生了什么”这件事从头到尾还原出来。这个模型不…

2026/10/14 4:36:54 阅读更多 →
2024年全国医院数据清洗实战:从5万条名单到区域医疗资源评估

2024年全国医院数据清洗实战:从5万条名单到区域医疗资源评估

做数据项目这些年,有一类数据始终绕不开,就是公共服务设施类数据。这次项目处理的是2024年全国省市县医院名单数据,总共5万条记录,核心字段包括医院等级、医院类型、床位数、年门诊量和医护人数。说是名单,实际用起来是…

2026/10/14 4:36:54 阅读更多 →
WPF Prism全特性实战:从MVVM到模块化架构设计

WPF Prism全特性实战:从MVVM到模块化架构设计

搞了几年 WPF,我陆陆续续用 Prism 搭过好几套内部项目。最近把一套老系统的壳重写了一遍,顺手把所有常用特性都过了一遍,做了一个相对完整的全特性 Demo,这里把整个设计和踩坑记录整理一下,给准备上手 Prism 或想系统补…

2026/10/14 4:36:54 阅读更多 →
每天介绍一家新质生产力公司51

每天介绍一家新质生产力公司51

https://mp.weixin.qq.com/s/PS1tnKdm84ZyKmiDXNozXA

2026/10/14 4:35:54 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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 阅读更多 →