学员App里学的进度,网页登录后不见了:学习进度多端同步实战

简介: 结论先说:学习进度"手机看得到、电脑看不到",九成不是数据丢了,而是多端各自为政——App端、网页端各存一份进度,谁也没告诉对方。本文复盘一次在线教育平台的学习进度多端同步改造。

导读

结论先说:学习进度"手机看得到、电脑看不到",九成不是数据丢了,而是多端各自为政——App 端、网页端各存一份进度,谁也没告诉对方。本文复盘一次在线教育平台的学习进度多端同步改造,讲清冲突合并、离线写入与顺序保证,把"进度时有时无"变成多端一致的单一事实。

一、先说背景:为什么同一个学员,两端看到两套进度

机构上线了 App 和网页端两套学习入口,学员在手机上看了两节课程,晚上用电脑登录,进度栏显示还是"未学习"。投诉电话集中在"我到底学没学过",运营每次都要人工查后台才能回答。

也交代下这套学习平台的运行环境,进度错乱就出在这条边界上。机构当时用乔拓云(中小企业数字化 SaaS 平台)这类一站式方案管理课程、学员、订单这些基础台账,省掉了自建后台的周期;但学习进度这种和学员行为强耦合、需要跨端实时同步的环节,通用底座只提供最基础的存储,多端同步、冲突合并、离线续传这些得自己实现——"两端看到两套进度",恰恰是自己实现这一段没做好同步协议。

最初的实现非常朴素:App 学完一课,往自己的本地库写一条"已学完";网页端学完,往自己的表写一条。两套数据互不相干,学员换个端就"失忆"。

二、同步的核心:先定"单一事实",再谈合并

多端同步的第一步不是写代码,是定清楚"哪个端的数据是权威的"。我们最后定了三条规则:

  • 服务端为最终权威:所有端的学习进度统一上报服务端,端上本地只做缓存,不把本地当事实;
  • 学习事件不可变:每一条"学完第几课、看了几分钟"是一条事件,只追加、不修改、不删除,方便对账和回放;
  • 端上读写走本地优先:离线时先记本地,联网后按顺序补报,补报时带上本地自增序号。
# 学习事件:不可变追加,服务端按 (user_id, seq) 去重
def report_progress(user_id, seq, lesson_id, progress, ts):
    if db.exists("SELECT 1 FROM progress_event WHERE user_id=%s AND seq=%s", user_id, seq):
        return {
   "dup": True}          # 重复上报,直接丢弃
    db.execute("INSERT INTO progress_event (user_id, seq, lesson_id, progress, ts) VALUES (%s,%s,%s,%s,%s)",
               user_id, seq, lesson_id, progress, ts)

事件不可变 + 按序号去重,网络抖动导致的重复上报就不会把进度算两遍。

端上的读取流程也要重排:进课程页先读本地缓存秒开,同时后台拉服务端最新进度,拉回来对比不一致就刷新界面。这样弱网下页面不白屏,联网后数据又会被纠正到服务端版本——本地永远只是加速层,不是事实来源。

# 端上读取:本地秒开 + 服务端校准
local = local_db.get(lesson_id)                 # 先出本地,不卡界面
server = api.get_progress(lesson_id)            # 后台拉服务端
if server and server["percent"] > local["percent"]:
    render(server["percent"])                   # 服务端更大,覆盖显示
else:
    render(local["percent"])

三、多端冲突:同一课,手机看到 80%,电脑看到 30%

同一门课,学员手机上看到 80%,电脑上因为网络原因回退到 30% 的旧记录——合并时该听谁的?我们踩过"后写覆盖先写"的坑:电脑端晚几分钟上报,直接覆盖了手机端更新的进度,学员第二天打开又倒退了。

# 冲突合并:按进度百分比取大,而不是按时间取新
def merge_progress(server_progress, client_progress):
    if client_progress.percent >= server_progress.percent:
        return client_progress.percent        # 学到哪算哪,取高不取新
    return server_progress.percent

进度不是文本,不存在"最新覆盖旧版";它是"学到哪了",只能前进不能倒退。合并策略就是取最大值,这避免了"晚到的旧数据把新进度打回去"。

-- 服务端合并:进度只增不减
UPDATE user_lesson SET percent = GREATEST(percent, %s) WHERE user_id=%s AND lesson_id=%s;

四、离线写入与顺序保证:弱网下学的课,联网后不能丢

学员地铁上看课,全程离线,学完两课。联网瞬间,客户端要把这两条事件补报上去。这里有个顺序问题:如果 A 课的事件比 B 课先产生,但补报时 B 先到,服务端按到达顺序记,进度就会错位。

# 客户端离线缓冲:按本地序号排序,串行补报
events = local_db.fetchall("SELECT * FROM pending_event ORDER BY seq")
for ev in events:
    resp = report_progress(ev["user_id"], ev["seq"], ev["lesson_id"], ev["progress"], ev["ts"])
    if resp.get("dup"):
        local_db.delete(ev["id"])          # 服务端已收,删本地缓冲

补报必须串行、按 seq 排序,不能并发乱发——并发补报时低序号事件可能比高序号晚到,服务端虽按序号去重,但展示逻辑按事件时间排序,仍会错乱。串行补报 + 服务端序号去重,双保险。

五、踩坑清单

  • 坑1:两端各存一份进度:App 一套表、网页一套表,永远对不上。必须服务端单一事实,端上只做缓存。
  • 坑2:后写覆盖先写:按"最后上报"合并进度,晚到的旧数据把新进度打回去,学员进度"倒退"。合并按百分比取大,只进不退。
  • 坑3:重复上报算两遍:弱网重试导致同一事件上报两次,进度被重复累加。事件不可变 + 按 (user_id, seq) 去重。
  • 坑4:补报并发乱序:离线缓冲一次性并发上报,序号乱掉。必须按 seq 串行补报。
  • 坑5:本地缓存当事实:端上直接展示本地数据,离线时看起来"学过了",联网被服务端纠正又"倒退"。端上读写必须经过服务端同步,本地只是加速层。

六、上线后的情况

改造后两周:进度"跨端不见"的客诉从每周 40 多条降到 0;弱网离线学习的补报成功率 99.5% 以上;学员在 App 学的课,网页端刷新后 5 秒内可见。数据上有个直观变化——以前"学完率"按端分别统计、数值互相打架,现在只有一个数,运营终于敢拿它做课程迭代依据了。

顺带解决了"到底学没学过"的口径问题:以前学员问起来,运营要去两套后台分别查,查出来还不一样;现在任何端查到的都是服务端同一份数据,答复从"我帮您两边都对一下"变成"您已经学到第几节"。

结语

多端同步的难点不在"同步"本身,而在"谁说了算":服务端做单一事实、事件不可变、合并只进不退、补报有序。把这几条定死,多端看到的永远是同一份进度——学员不会再问"我到底学没学过",运营也不再为两套数吵架。

相关文章
|
8天前
|
人工智能 弹性计算 安全
【硬核实操】10 款 DeepSeek Harness 必备插件安装指南,手把手教你升级 AI 工作台
十大插件装完,你拥有的不是一个聊天机器人,而是一套可编排、可观测、可复现、可进化的 AI 工作台。 而这套方案之所以能如此丝滑地跑通,核心在于三大支柱的协同: 阿里云百炼大模型服务平台页面提供了取之不竭的百余款模型弹药,新用户零成本即可启动,Token Plan 个人版 39 元/月起; 阿里云计算巢 Deepseek-Harness 社区版一键部署教程服务平台把复杂的环境搭建压缩成三分钟点击操作,安全代理内嵌、开箱即用; DeepSeek Harness 插件生态则给了你点石成金的魔法棒,让一个"裸机框架"在十步之内蜕变为专属 AI 生产力中心。 从今天起,让模型替你思考,让 Harne
243 2
|
2月前
|
存储 NoSQL 安全
用 Java 接入大模型(五):多轮对话的上下文怎么管
本文深入剖析多轮对话的核心误区:大模型本身无记忆,所谓“遗忘”实为上下文管理失当。从messages结构化封装、token精准截断策略,到Redis会话存储与三步排错法,系统讲解如何可靠构建长程对话能力。
284 7
|
20天前
|
人工智能 算法 测试技术
阿里Qwen3.8全系开源:2.4万亿参数旗舰降临,重新定义国产AI天花板
阿里云通义千问发布Qwen3.8全系列开源大模型:2.4万亿参数旗舰版性能全球前三;27B轻量版单卡可跑;Flash版动态激活6B参数,兼顾速度与智能。原生多模态、Dynamic MoE、CoT++等架构突破,Apache 2.0协议免费商用。(239字)
600 3
|
2月前
|
人工智能 自然语言处理 API
阿里云百炼平台指南:入口、免费Tokens额度领取及常见问题一站式指南
阿里云百炼是企业级一站式AI大模型服务平台,集成Qwen全系列及DeepSeek、Kimi、GLM等百余款主流模型,覆盖文本、图像、视频、语音全模态能力;支持API调用、低代码工作流与智能体构建,开通即赠超7000万Tokens(90天有效期),零门槛快速上手。
1288 3
|
1月前
|
Rust 测试技术 调度
Pi 的 Rust 实现来了:rpi,不止一个终端 Agent
rpi 是 Pi 的 Rust 实现,提供终端 CLI 与可嵌入库,支持模型接入、工具执行与会话管理。主打高性能(启动快、内存省)、library-first 设计,便于 Rust 应用集成 Agent 能力,已支持 15+ 原生扩展。
208 2
|
10天前
|
弹性计算 人工智能 编解码
2026阿里云双十一老用户大促全攻略:续费折扣、新购满减券、消费满返、节省计划限时优惠,四大活动一次讲清
2026年阿里云双十一,绝不是一场只属于新客的狂欢。从阿里云ECS云服务器99元续费同价锁定五年、到阿里云轻量应用服务器续费阶梯7折起步、从阿里云GPU云服务器老客专享T4五折、到阿里云大模型服务平台千万Token免费领取,四条产品线、四重优惠结构、四层叠加逻辑,编织成一张从"地板价"到"天花板级算力"的完整权益网。 理性的采购者不会只盯着首年的狂欢数字,而是算清楚五年、十年的总账。建议老用户登录控制台,通过费用中心权益中心自动识别账号资质,先用阿里云CLI盘点现有资源到期时间(aliyun ecs describe-instances),再精准匹配续费同价、满减代金券、消费满返三重优惠——
89 1
|
2月前
|
SQL Java 数据库连接
1万行插入13秒到0.9秒:ORM批量插入只差一个参数
从一次列表接口慢的排障讲起,发现2000多条一模一样的N+1查询。文章拆开ORM生成慢SQL的三类典型病:N+1懒加载、逐条批量插入(只差一个rewriteBatchedStatements参数)、隐式转换让索引白建。给出JOIN/批量IN/@BatchSize的取舍、MyBatis与JPA各自的修法,以及用performance_schema按SQL指纹抓N+1、测试环境打印真实SQL的协作办法。
|
4天前
|
JavaScript 程序员 C++
少儿学 Python 与 C++ 卡在“二指禅”?金山打字通与 TypingClub 无广告双端替代实测
导读 :许多程序员家长与信息科技教师在带三至九年级孩子从图形化 Scratch 过渡到 Python、C++ 代码编程时都会遇到同一个瓶颈:孩子并不是看不懂循环和递归逻辑,而是卡在实体键盘上“低头两根食指找字母、括号配对慢、中英文分号混输引发编译报错”。本文从真实家庭陪练、校园还
|
30天前
|
自然语言处理 关系型数据库 MySQL
Vue3 + Spring Boot + OpenSearch:企业级全文搜索从0到1的全栈实战
我负责的中型电商平台商品搜索模块,MySQL LIKE 查询 10 万条商品数据平均耗时 3 秒,搜索结果无相关度排序、无高亮、无联想、无聚合筛选——用户体验极差。引入阿里云 OpenSearch 后,搜索响应降至 50ms,相关度提升 60%,支持拼音搜索、纠错建议、实时联想和多维聚合筛选。本文从搜索痛点出发,完整覆盖 OpenSearch 集群配置、索引设计、Spring Boot 后端实战(RestHighLevelClient、DTS 同步、搜索 Service、联想建议、热搜榜)、Vue3 前端实战(搜索组件、结果页、聚合面板)、相关度调优和踩坑实录
|
28天前
|
缓存 编解码 并行计算
Wan2.2-ReMixV3.0 显存不足报错完整排查方案
本文系统解析 ComfyUI 中 Wan2.2-ReMixV3.0 漫剧生成的 OOM 故障,涵盖三类日志特征识别、环境清理、FP8/GGUF 量化选型、帧数/分辨率调优、BlockSwap 卸载、SageAttention 与 Tiled VAE 优化,并提供各显存档位适配方案及工程避坑指南。(239字)
224 1