学习打卡功能开发实战:从表结构设计到线上踩坑全记录
前段时间接了一个学习类App的项目需求池里躺着一个编号3.10的任务名字就四个字学习打卡。当时团队里不少人都觉得这不就是给用户一个按钮每天点一下签个到吗结果真进入开发阶段才发现这个看似人畜无害的功能牵涉到的细节远比想象中多——连续天数怎么算、跨天怎么处理、补卡规则怎么定、并发点击会不会刷出多条记录、时区不一样的人打卡时间算哪一天……每一个点都能让测试同学头大三天。这篇文章就把我从需求梳理到上线维护的完整过程记录下来包括你直接能拿去用的表结构、接口设计和代码逻辑也有我在实际操作中踩过的一些坑。内容适合正在做学习类产品、社区养成类模块的产品经理、前后端工程师和测试同学哪怕是刚入门的新人也能照着思路落地。1. 需求拆解与整体设计思路学习打卡不是一个“点按钮”的事1.1 把模糊需求翻译成可落地的功能清单项目最初的描述只有寥寥几行“3.10 学习打卡用户每天打卡记录学习状态展示连续天数。”连打卡范围是什么都没有说清楚。我在做技术方案之前先拉着产品随手写了一份功能清单这一步看起来不算技术活但对后续开发影响极大。打卡功能拆下来至少包含几块打卡动作、打卡记录查询、连续天数统计、积分或激励、提醒机制以及带争议的补卡。对照MVP优先级打卡动作和记录查询是必须项连续天数统计是核心卖点积分激励可以先做最简单版本提醒机制可以二期接补卡则是建议直接藏起来不要上线首版就放开。这样排序的理由很简单学习打卡的核心用户动线是“记录-反馈-坚持”先保证这一条链路稳定再叠加花活。清单确定后我建议团队用“业务日”而不是“自然日”来定义打卡归属。举个例子凌晨0点30分打的卡算头一天晚上还是当天大部分用户心里的“今天”是早上醒来之后的时间。如果按服务器时间0点切天就会有人因为零点打卡被分到前一天体验很怪。因此我们约定业务日按用户所在时区的自然日计算切割点是当地时间的0点。这个约定后续省了很多解释不清的坑。1.2 技术选型为什么是这套组合打卡场景的技术特点是写多读多、数据量增长快、状态简单。用户每天至少打一次卡一年就是365条记录看起来不多但用户数量过万后就是几百万行。考虑到团队最熟的栈是Java后端加MySQL我在技术选型上并没有追求新奇而是保留了常规但稳妥的组合Spring Boot作为服务端框架MySQL做持久化存储Redis做连续天数和热数据的缓存。前端侧因为产品需要同时覆盖小程序和App选择了跨端框架来统一代码。有人会问为什么不用MongoDB或者专门的时序数据库来存打卡记录。我考虑过但最终放弃了。打卡记录本质上就是“用户ID日期状态”的简单结构用MySQL关系模型非常好表达而且团队对MySQL的运维和排障经验成熟出了问题容易定位。Redis则盯准了连续天数计算的高频读需求用户每次打开打卡页首页就要展示连续打卡天数如果每次都去MySQL里扫表回溯压力不值得。用Redis存一份缓存命中就直接返回穿透时再回源计算这套逻辑在团队里是验证过的。还有一个容易被忽视的决策打卡记录不要直接接消息队列异步写。很多设计者喜欢把打卡这种低操作成本的请求扔进MQ觉得能削峰但这会让用户打卡后立刻看到“成功”和后续数据不一致之间出现时间差。学习打卡的反馈链路要求秒级一致用户点了按钮页面马上变成已打卡状态。所以打卡接口走同步写MySQL确认成功后再更新Redis异步可以留给积分发放这类非核心动作。2. 核心数据模型与边界细节把连续天数算明白2.1 打卡记录表应该怎么设计数据表是整个打卡功能的地基。我设计的表结构如下CREATE TABLE check_in_record ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL COMMENT 用户ID, check_in_date date NOT NULL COMMENT 业务日期按用户时区, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, source varchar(20) NOT NULL DEFAULT app COMMENT 打卡来源app/h5/mini_program, is_makeup tinyint(1) NOT NULL DEFAULT 0 COMMENT 是否补卡, PRIMARY KEY (id), UNIQUE KEY uk_user_date (user_id, check_in_date), KEY idx_user_month (user_id, check_in_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;几个关键点要解释清楚。第一是唯一索引 uk_user_date它负责挡住重复打卡请求。无论前端按钮被用户点多少次接口被并发请求多少次数据库层面就能保证同一个用户同一天最多只能有一条有效记录。第二是check_in_date 与 created_at 分离created_at是服务器时间用来做审计和排查check_in_date才是用户可见的业务日期真正决定打卡归属哪一天。第三是is_makeup字段为后续可能的补卡功能预先留口子首版可以先不放但表结构有一列不会增加成本。积分变动的表也跟着设计好了避免以后要查用户积分流水时抓瞎CREATE TABLE point_change_log ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, change_value int(11) NOT NULL COMMENT 变动值正负, biz_type varchar(30) NOT NULL COMMENT 业务类型check_in/check_in_extra, biz_id bigint(20) DEFAULT NULL COMMENT 关联业务ID, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表的核心是biz_id关联打卡记录ID保证积分流水可追溯排查数据不一致时能快速定位是哪一笔业务产生的积分。2.2 连续天数计算边界条件比你想的多连续打卡天数是最容易出Bug的地方。常见的错误算法是“拿今天往前数只要每天都打卡就1”但这里隐藏着大量边界情况我实际处理时至少遇过这几种跨年跨月连续天数不能只查当月的记录需要回溯到上月甚至上年。空档判断用户昨天没打卡但前天连续打了10天今天再打卡连续天数应该是1而不是把历史加起来。未来日期用户手动调整设备时间到明天然后打卡系统必须拒绝否则连续天数会被“预支”。时区差异同一个服务器部署在全球用户可能在不同时区打卡日期不能一刀切用UTC。补卡影响补卡当天算不算连续规则不同结果完全不同。我最后实现的连续天数算法逻辑并不复杂但每一步都要想清楚。核心思路是以“当前业务日”为锚点往前遍历最近的一条打卡记录如果这个日期不是今天也不是昨天那么连续天数直接归零或为1如果是正常连续轨迹再继续往前找相邻日期的记录直到出现断档。2.3 Redis缓存键设计与更新策略连续天数的高频读取靠Redis缓存。我设计了两个键checkin:streak:{userId}值为连续天数checkin:last_date:{userId}值为最近打卡的业务日期例如“2026-01-15”。只要有了“最近打卡日期”和“连续天数”就能快速判断用户今天是否已打以及今天打了之后连续天数应该加几。更新策略要注意一个细节用户打卡成功当天不要直接累加连续天数而是先判断昨天是否打卡。如果昨天有记录新连续天数才是旧天数1如果昨天没记录说明从今天重新开始连续新值就是1。这个判断不能省否则就会出现用户隔天打卡但连续天数还是累加的情况。为了性能当用户查询打卡状态时先用Redis拿数据如果Redis里没有对应键再回源MySQL扫描最近30条记录做回溯计算算好之后回填Redis。这个回源逻辑一定要加分布式锁或者设置短过期时间防止多个请求同时回源把数据库打爆。我从实战中得到的经验是锁的粒度细到用户维度就够用了不需要全局锁。3. 从数据库到接口到前端打卡功能的落地实现3.1 接口定义把业务逻辑收敛到后端后端接口我一共做了三个定位都很清晰。打卡接口POST /api/v1/checkin Content-Type: application/json { source: app, clientTime: 1734256800000 }这里的clientTime是客户端当前毫秒级时间戳主要作用是服务端辅助判断用户设备时间和服务器时间的偏差真正决定业务日期还是后端根据自己的业务日规则来算防止用户篡改设备时间刷连续天数。记录查询接口GET /api/v1/checkin/records?year2026month1返回当月每天的打卡状态列表前端用来渲染日历热力图接口返回数据量很小也不需要分页。汇总接口GET /api/v1/checkin/summary返回连续打卡天数、累计打卡天数、今日是否已打卡首页直接拉这个接口就够。接口层面还有一个约定所有接口必须返回统一的响应结构包含code、message、data三个字段。打卡接口的成功code是0错误码给到10001代表“重复打卡”10002代表“日期非法”。这个约定让前端处理逻辑时不用猜。3.2 打卡接口的核心实现逻辑服务端处理打卡请求时我按下面这个顺序做每一步都不能省第一步校验入参。clientTime不能为空source限定在枚举范围内。第二步计算业务日期。根据userId获取用户所在的时区配置然后用这个时区算出用户当前的业务日期格式如“2026-01-15”。用户时区的获取优先从用户表中读取读不到就按默认时区处理但一定要在日志里记一条warn方便排查。第三步查重。通过check_in_record表检查唯一索引直接INSERT即可如果捕获到DuplicateKeyException就说明重复打卡了返回错误码10001。这里不要先SELECT再INSERT两步操作有并发窗口唯一索引才是最终防线。第四步更新Redis。拿到今日打卡成功后读取checkin:last_date:{userId}如果last_date等于今天的业务日期说明是重复请求被Unique拦截如果last_date等于昨天的日期说明连续轨迹没断streak加1否则streak重置为1。然后更新last_date为今天的业务日期。第五步发送积分奖励。积分变动不放在主链路里同步执行而是直接写point_change_log表如果依赖外部系统再加MQ异步否则会拖慢打卡接口响应时间。核心代码逻辑用Java伪代码表示如下public CheckinResult checkin(Long userId, String source, Long clientTime) { LocalDate bizDate userTimeZonePolicy.getBizDate(userId, clientTime); CheckInRecord record new CheckInRecord(); record.setUserId(userId); record.setCheckInDate(bizDate); record.setSource(source); try { checkInRecordMapper.insert(record); } catch (DuplicateKeyException e) { return CheckinResult.duplicate(); } Long newStreak calculateStreakAfterCheckin(userId, bizDate); redisService.set(checkin:last_date: userId, bizDate.toString()); redisService.set(checkin:streak: userId, newStreak); pointService.recordAsync(userId, 10, check_in, record.getId()); return CheckinResult.ok(newStreak); }这里唯一“容易慌”的点是重复插入的异常处理很多新人喜欢先查再插结果并发一高就出重复数据。我自己踩过这个坑之后就只信数据库唯一索引了。3.3 前端交互与状态呈现前端打卡页的核心元素有三个主打卡按钮、连续天数卡片、日历热力图。按钮的状态我设计成三种未打卡可点击、已打卡置灰并展示“今日已打卡”、补卡中可选首版不做。要注意按钮点击的交互细节点击后立刻置灰并进入“打卡中”加载态等接口返回成功后再展示连续天数动画。不要等接口失败了才解除置灰而是要定义一个失败后的重试状态防止用户连续快速点击造成重复请求。虽然数据库有唯一索引兜底但能减少无效请求终归是好的。日历热力图我直接用了一个现成的日历组件然后按月份拉打卡记录。渲染逻辑是遍历当月每一天如果当天有记录就高亮同时用颜色深浅区分是否连续比如连续1到3天浅绿4到7天中绿8天以上深绿。这部分前端实现不难难的是用户对“连续”的心理感知所以我会在连续天数卡片上放一个“再坚持N天获得徽章”的进度条让用户看到明确目标。4. 线上踩坑实录打卡数据为什么会乱4.1 打卡时间总是不对时区问题排查项目上线两周后运营反馈有海外用户打卡日期显示乱码明明是当地时间1月15日晚上10点的打卡App里却显示成1月16日。查了一圈发现后端接口用服务器时区默认UTC8来计算业务日期而用户位于UTC4时区。服务端按北京时间已经进入1月16日但用户当地时间还是1月15日晚上10点因此记录被打到了“明天”。这个问题不只在海外用户身上出现国内跨时区旅行的人也会遇到。解决方法是把用户时区作为基础信息存进用户表打卡时取用户当前时区的日期作为业务日。如果取不到时区默认使用服务端所在时区兜底但必须给用户提供手动纠正入口。上线这个小修复之后乱码问题就消失了。4.2 并发点击引发的重复打卡刚开始上线时有用户反馈自己只打了一次卡积分却加了两倍。查日志发现是用户连续快速点击了打卡按钮前端虽然做了置灰但有个旧版本客户端没有做状态控制导致同时发出3个请求。三个请求全部打到后端唯一索引拦截了后面两个插入但由于积分发送是异步的前两个请求在唯一索引判断之前都已经执行到了积分发送逻辑结果就重复加了积分。这个案例说明一个道理幂等不能只靠唯一的数据库约束业务逻辑也要做幂等。后来我把积分发放改成由打卡记录ID驱动通过biz_id字段的唯一性避免重复发放在point_change_log表上加了唯一索引uk_user_biz_type_bizid保证同一张打卡记录只能产生一条积分流水。4.3 补卡功能带来的统计难题产品中期提了一个需求允许用户每月补卡3次理由是用户可能会因为突发事件错过当天打卡。我没有直接反对但提了一个方案补卡操作只能补充“过去的日期”不能补“未来日期”每月有次数上限补卡不产生积分或者积分减半补卡后连续天数以实际记录为准但补卡动作本身要打上is_makeup标记方便后续审计。上线之后果然出了Bug。有用户在月底最后一天补了月初漏打的卡直接导致“月打卡率”统计口径出现两条线按打卡记录数统计和按业务日统计结果互相矛盾。后来跟运营对齐了规则月打卡率分母是当月天数分子是当月实际打过卡的天数补卡记录按打卡日计算不按补卡操作日计算。规则一旦定清楚统计口径就稳了前端展示的数据也不再忽高忽低。4.4 一套线上排查清单打卡功能上线免不了要处理线上异常。我整理了一份排查步骤测试和运维照着做基本能快速定位问题查用户反馈的具体时间点先看服务端日志中该用户ID的打卡请求确认是否进入后端返回码是什么。查MySQL中该用户在对应业务日的打卡记录是否存在注意区分是完全没有记录还是记录被覆盖。查Redis中checkin:streak和checkin:last_date两个键的值与MySQL记录比对不一致时以MySQL为准手动修正Redis缓存。查point_change_log表确认积分流水是否有重复或缺失特别注意biz_id是否关联到了正确的打卡记录ID。所有修复动作执行前先导出相关记录备份操作过程全程留日志不要在客户环境和数据无备份的情况下直接改库。这套清单救过我很多次尤其是“Redis和MySQL不一致”的场景多半是缓存更新失败或者回源计算错误导致的修正缓存后问题就解决了。5. 打卡功能上线后的迭代与扩展思考5.1 从打卡记录里还能挖掘什么打卡功能稳定后我就在想这些数据除了展示连续天数还能怎么用。后来发现打卡时间分布本身就是一个很有价值的维度有人习惯早起打卡有人习惯睡前打卡还有人是固定午休时间。这些行为习惯可以通过打卡时间戳聚类分析出来然后给不同用户匹配不同的提醒策略。比如早起的用户提醒时间放在早上7点前夜猫子用户提醒放在晚上10点前。这样个性化之后有效打卡率确实涨了几个百分点。再进一步连续打卡超过30天的用户和频繁断卡用户的推送文案也要不同前者更愿意接受挑战式激励后者需要更多宽容感比如“断卡也没关系今天重新开始”会比“你已经断了3天”更友好。5.2 习惯养成设计里的一个反直觉经验最后再分享一个实际操作中得来的经验打卡功能上线后最担心的不是技术稳定性而是用户为了刷连续天数钻空子。有一段时间有人用修改设备时间的方式提前打卡“预支”连续天数逼得我们强制做了服务器时间校验前端必须把设备信息和打卡动作绑定校验。这件事让我意识到习惯养成类功能必须在“激励”和“防作弊”之间找平衡。后来我们还加了一个“免打卡日”机制允许用户每周有一天不打卡也能保留连续天数。一开始团队担心这样做会降低活跃度结果实际数据反而更好——给了当天确实忙不过来的用户一个缓冲他们不会因为断了一次就直接放弃整体留存反而上升。如果你也在做类似功能可以考虑把这个设计提前放进去这比事后补卡机制更有效也更容易向用户解释规则。学习打卡这个项目做下来我最大的体感是一个功能的大小不在于代码量而在于边界条件的数量。能把“每天点一个按钮”背后这些细节都想清楚在数据层、接口层、展示层都做好对应处理这个功能才真正立住了。

相关新闻

AI敏捷招聘引擎:用行为数据和预测模型重构2026人才决策

AI敏捷招聘引擎:用行为数据和预测模型重构2026人才决策

我做了六年多的人力资源数字化系统建设,从最初的ATS选型到如今基于行为数据的预测模型,一路踩坑一路迭代,发现一个残酷的现实:传统的招聘方式,已经撑不起2026年的人才竞争了。你可能还在用"简历关键词匹配面试官直…

2026/10/18 4:39:32 阅读更多 →
告别帐号限制,OpenAI Codex 零基础速通,配合 Codex++ 完美解锁 TaoToken 配置

告别帐号限制,OpenAI Codex 零基础速通,配合 Codex++ 完美解锁 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 4:39:32 阅读更多 →
DH2URDF:机器人DH参数转URDF的自动映射与避坑指南

DH2URDF:机器人DH参数转URDF的自动映射与避坑指南

简介:DH2URDF 是一份将 Denavit-Hartenberg 参数表转换为统一机器人描述格式(URDF)的 JavaScript 工具资源,面向机器人建模、运动学仿真与 ROS 学习方向的开发者及学生。它解决的是手工编写 URDF 繁琐、易错的问题:只需…

2026/10/18 4:39:32 阅读更多 →

最新新闻

国庆AI行业盘点:模型更新、开源生态与智能体落地加速

国庆AI行业盘点:模型更新、开源生态与智能体落地加速

国庆长假这两天,AI圈跟过年似的。10月1日到10月8日,前后八天,不断有模型更新、开源仓库爆点、智能体应用密集落地。作为一个长期跟踪这条赛道的人,我几乎每天都要刷好几遍测试榜单和社区讨论串,很多信息还没来得及沉淀…

2026/10/18 5:37:42 阅读更多 →
程序员必备20个网站:技术阅读、刷题、接单与效率工具盘点

程序员必备20个网站:技术阅读、刷题、接单与效率工具盘点

程序员必备网站这个老话题,几乎每隔几个月就会被重新拿出来聊一轮。技术、接单、刷题这三类网站,每个开发者的收藏夹里都躺着不止一个,但真到要用的时候,很多人会发现:收藏了 300 个网站,能稳定打开、准时解…

2026/10/18 5:37:42 阅读更多 →
全大写提示词触发AI风控?解析Claude互审封禁真相与安全写法

全大写提示词触发AI风控?解析Claude互审封禁真相与安全写法

有意思。“让Claude管Claude”本身没问题,问题是这句提示词是用全大写敲出来的。我是在一个技术讨论群里看到这件事的:某人做了个自动化流程,让一个Claude会话去审核另一个Claude会话生成的文案,这本来是个很常见的质检思路&#…

2026/10/18 5:37:42 阅读更多 →
Image GPS Editor实战:照片GPS数据的读取、修改与批量处理

Image GPS Editor实战:照片GPS数据的读取、修改与批量处理

前阵子帮朋友整理一趟长途自驾的素材,几百张照片导进电脑,结果发现相机里明明开了GPS记录,出来的照片却有一大半坐标信息是空的。另一批用手机拍的照片倒是带位置,但其中有几张因为当时在船上漂着,定位偏到了海里。用看…

2026/10/18 5:37:42 阅读更多 →
变量之路:理清编译与链接中的符号、地址与重定位机制

变量之路:理清编译与链接中的符号、地址与重定位机制

先问一个问题:你在 IDE 里写了int counter 0;,点一下运行按钮,程序跑起来了。可是那个叫counter的变量,它的地址到底是谁给它的?它为什么能从一个名字变成一个真实存在于内存里的地址?我在带新人的时候发现…

2026/10/18 5:37:42 阅读更多 →
律所案件管理系统设计与落地:角色权限、流程引擎与文书协同实践

律所案件管理系统设计与落地:角色权限、流程引擎与文书协同实践

1. 这个系统到底在解决什么问题做律师的朋友应该都有过这种体验:一个案子从收到材料到最终归档,中间要经过立案、排期、取证、文书起草、审核、盖章、送达、开庭、归档这么一串流程。传统做法是什么?微信群接龙、Excel表格、共享文件夹、Outl…

2026/10/18 5:36:42 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →