Unilearning统一在线学习平台
面向企业、机构与个人的在线学习 SaaS。用 Go 模块化单体重构:8 个业务域(auth / cms / eas / ocs / oms / osm / quiz / ai)进程内聚合,把原先 6 个微服务收进 1 个二进制、1 个容器,域间只认 api 契约,internal 墙由编译器强制。
微服务拆得够碎之后,真正的成本在服务间调用。模块化单体把调用变回函数调用,同时用编译器守住域边界。
从课程上架到 AI 批改,教学闭环里的每个环节都是一个可独立演进的业务域。
它到底解决什么问题
四条主线能力,对应四类真实存在的业务诉求。
课程、课件、排课、作业、考试、评论覆盖教与学的全流程,进度与成绩实时可查。
八大业务域进程内聚合,域间只认 api 契约,internal 墙由编译器强制,不是靠 lint 自觉。
教师触发单份或整班批量 AI 批改,草稿挂在扩展属性上不触碰业务状态,终审权始终在人。
版本化 SQL 迁移幂等升级,老系统数据一键导入,老用户原密码直接登录。
能力一览
- 课程树 + 学习记录:物化路径树,进度沿大纲祖先链自动冒泡汇总
- 四类课件:content、page、video、media,统一进度记录与断点续学
- 题库与考试:单选 / 多选 / 判断组卷,交卷即判分、即出解析
- AI 首轮批改:LLM 生成批改草稿,教师终审拍板,AI 永不替教师做决定
- 在线商城:课程商品化交易闭环,订单即购买凭证
- 教务排课:班级、成员与排课管理,三端路由与旧系统前端兼容
这不是 PPT 里的产品
下面的数字来自 unile-go 仓库的真实代码:模块、数据表、接口与能力项。
技术栈
功能清单
8 个业务域、48 项具体能力。每一项都对应代码里真实存在的实现。
课程树与学习记录
cms 域大纲是一棵树,学完一节,整条祖先链的进度自动更新。
- 物化路径树:课程大纲以路径编码存储,子树查询不需要递归
- 学习记录沿祖先链冒泡 upsert,父节点进度自动汇总
- 节点类型可扩展,试卷可通过 node_type=quiz 挂在大纲上
- RecordService.UpdateAttributes 合并写入扩展列,AI 草稿不污染业务字段
- 进度与成绩实时可查,学员端与教师端共享同一份事实
四类课件
ocs 域图文、页面、视频、媒体四种形态,进度口径统一。
- content:图文内容课件
- page:自定义页面课件
- video:视频课件,支持断点续学
- media:音视频等其他媒体形态
- 统一的课件学习进度记录模型
教务排课
eas 域班级、成员、课表,教务的日常。
- 用户档案:学员与教师身份统一管理
- 班级管理:班级 CRUD 与成员关系
- 成员角色:教师、助教、学员多角色
- 排课:schedules 表关联班级与大纲节点
- expand 参数兼容:teachers / assistants / students 一次取回
题库与考试
quiz 域客观题当场判分,主观题留给教师。
- 题库按课程隔离,单选 / 多选 / 判断三种题型
- 选项与答案走 JSONB,选项索引 0 起始
- 整卷组卷:quiz_paper_questions 关联试卷与题目
- 交卷即判分:集合比对纯函数实现,服务端自动完成
- 交卷即出解析:响应返回每题对错、正确答案与解析
- 取卷剥离答案:学员取卷接口不返回 answer / analysis
- 多次作答:attempt 保留历史,进度取最新、分数取最高
- 判分与进度写入解耦:进度失败不回滚判分,标记 sync_record_failed
AI 批改
ai 域AI 写草稿,教师做决定——这条边界写在实现里。
- 单份触发:POST /tv1/teacher/homeworks/:hid/ai-feedback
- 整班批量:POST /tv1/teacher/classes/:cid/ai-feedback,分块处理,上限 100
- 只处理 submitted 状态,已有草稿的自动跳过,不重复烧 Token
- 单份失败不中断整批,逐条报 error,下次自动重试
- 草稿写入 record.attributes.ai_feedback,不触碰业务状态列
- 终审仍走既有 feedback 接口,AI 永不替教师做决定
- LLM 客户端只用标准库实现 OpenAI 兼容协议,GLM / DeepSeek / 本地 Ollama 均可
- 未配置时接口返回 501,不影响其它功能
评论与社区
oms 域盖楼式评论,子树查询一次拿走。
- 盖楼树结构:root / path / depth 三字段支撑层级
- 子树查询:一次查询取回整棵子树
- 评论挂载在课程与节点上
- 移植自旧系统,核心算法有单元测试覆盖
在线商城
osm 域课程变成商品,订单即购买凭证。
- 商品管理:课程快照制商品,价格以分为单位
- 商品状态机:draft → on_sale ⇄ off_sale
- 学员端货架只展示 on_sale 商品
- 下单:商品快照落库,订单即凭证
- 订单状态机:pending → paid | cancelled,paid → refunded
- 管理端可线下收款确认(MarkPaid)与退款
- 订单无删除接口,作为财务凭证保留
身份与迁移
auth 域 · server换引擎不换轮胎,老系统数据直接可用。
- local 模式:bcrypt + HS256 JWT 内建,开箱即用
- remote 模式:切换为对接 UIAM,edge 与各域代码不动
- 版本化 SQL 迁移:每域一组 pg / mysql 方言文件,启动时自动幂等升级
- 迁移版本记录在库内 unile_migrations 表,已应用自动跳过
- legacy-import 工具:旧库一键导入,老用户 bcrypt 原密码直接登录
- OSS STS 凭证接口:支持真实 AssumeRole 与 static 两种模式
接口契约
12 个代表性接口。列在这里的是对外契约的骨架,完整定义随部署提供。
/uv1/auth/login学员端登录:local 模式 bcrypt,或 remote 模式转 UIAM
/uv1/courses/{id}/outline课程树大纲,物化路径一次取回整棵子树
/uv1/records提交学习记录,进度沿物化路径的祖先链自动冒泡
/uv1/nodes/{id}/paper取卷:服务端剥离 answer / analysis 后再返回
/uv1/nodes/{id}/attempts交卷:同一事务内判分并返回逐题解析
/tv1/teacher/homeworks/{hid}/ai-feedback单份 AI 批改,草稿写入 attributes 扩展列
/tv1/teacher/classes/{cid}/ai-feedback整班批量 AI 批改,分块处理,单批上限 100
/tv1/teacher/homeworks/{hid}/feedback教师终审,决定权始终在人
/admv1/shop/products管理端商品管理,状态机 draft → on_sale ⇄ off_sale
/uv1/orders下单:商品快照落库,订单即购买凭证
/admv1/orders/{id}/mark-paid线下收款确认,订单状态机流转
/uv1/nodes/{id}/comments盖楼评论:root / path / depth 树形结构
一次真实调用
接口长什么样,比接口清单更能说明问题。
01$ curl -X POST https://learn.zhenbei.tech/uv1/nodes/146/attempts \02 -d '{"answers":[{"qid":9,"choice":[0,2]},{"qid":10,"choice":[1]}]}'0304{ "score": 50, "total": 100, "graded": "instant",05 "detail": [ { "qid": 9, "ok": false, "answer": [0,1], "analysis": "…" },06 { "qid": 10, "ok": true } ],07 "progress": { "node": 146, "bubbled": 12 } }契约纪律
接口是承诺,不是实现细节的暴露。这三条规则我们一直在守。
破坏性变更走大版本,提前一个版本周期公告。已发布的接口不会因为内部重构而改语义。
错误码语义化而不是笼统 500,调用方(包括 AI Agent)能据此决定是重试还是放弃。
默认返回结构化数据而不是给人类看的文本,这样 AI 能直接读,不需要人截图转述。
功能模块
5 组、11 个代码模块,全部来自 unile-go 仓库的真实目录结构。
server/ + 各域模块教什么、学什么、学到哪了。
cms课程树(物化路径)与学习记录,进度沿祖先链冒泡 upsert
cms/internal/bizocs四类课件:content / page / video / media,及课件学习进度
ocs/internal/biz谁在教、谁在学、什么时候上。
eas教务:users、classes、class_user_relations、schedules 四表全量覆盖
eas/internal/biz学了没学会,要有办法验证。
quiz题库(单选/多选/判断,options/answer 走 JSONB)+ 试卷组卷 + 作答判分
quiz/internal/bizaiLLM 批改:OpenAI 兼容协议,纯标准库实现,零新增依赖
ai/internal/biz学习不是孤独的,课程也可以被买卖。
oms盖楼评论:root / path / depth 树形结构 + 子树查询
oms/internal/bizosm在线商城:商品上架、下单、支付、退款与订单管理
osm/internal/biz边界由编译器而不是文档守住。
auth身份:local 模式 bcrypt + HS256 JWT,可平滑切换 remote 对接 UIAM
auth/internal/bizedge聚合层:uv1 / tv1 / admv1 三端路由,路径保持旧前端兼容
edge/base公共类型:分页、Attribute 扩展列、统一错误码,零外部依赖
base/server装配层:配置、迁移运行器、依赖注入与启动
server/分层设计
从接入到运行时,每一层负责什么、用什么实现。
server:配置、迁移、依赖注入与启动,Dockerfile 单二进制交付
edge:uv1 学员端 / tv1 教师端 / admv1 管理端,跨域编排只写在这里
cms 课程树与学习记录 · ocs 四类课件与学习进度 · eas 教务排课
oms 盖楼评论 · osm 课程商城 · quiz 题库与考试 · ai LLM 批改
auth:local bcrypt + HS256 JWT 内建,可切 remote 对接 UIAM
base:分页、Attribute、错误码,零外部依赖
PostgreSQL / MySQL 双方言,每域版本化迁移自动升级
关键流程
最重要的几条路径,逐步拆开看。
学员学完一节课
一次提交,整棵大纲树的进度同步更新。
- 1
学员在 uv1 提交某个节点的学习记录
- 2
cms 校验节点归属与课程权限
- 3
记录 upsert 到学习记录表
- 4
进度沿物化路径的祖先链向上冒泡汇总
- 5
父节点与课程级进度同步刷新
- 6
学员端大纲视图与教师端进度视图同时可见新进度
在线考试交卷即出分
客观题不需要等老师批。
- 1
学员请求挂载 quiz 的节点,服务端剥离答案与解析后返回
- 2
学员作答提交到 /uv1/.../attempts
- 3
edge 校验节点确实挂卷,转交 quiz 域
- 4
quiz 域用集合比对纯函数逐题判分(未作答 = 错,0 分)
- 5
attempt 与全题明细在同一事务落库,total 为交卷时快照
- 6
cms Records.Save 写入分数与完成状态,进度沿祖先链冒泡
- 7
响应返回完整判分明细:每题对错、正确答案与解析
AI 批改与教师终审
AI 提速,但决定权在人。
- 1
学员提交作业,状态为 submitted
- 2
教师在作业视图触发单份或整班批量 AI 批改
- 3
edge 组装上下文:课程、节点、作业要求、课件标题
- 4
ai 域调用 LLM 生成批改建议(已有草稿的跳过)
- 5
草稿写入 record.attributes.ai_feedback,业务状态列不动
- 6
教师在作业视图看到 AI 草稿
- 7
教师走既有 feedback 接口做终审,草稿保留在扩展列备查
技术栈
性能与规模
这些数字不是估算,是从代码与运行配置里读出来的。每一条都注明了它的实际含义,避免被当成宣传口径。
unile-goauth / cms / eas / ocs / oms / osm / quiz / ai 进程内聚合
原先 6 个微服务收进 1 个二进制、1 个容器
域间只认 api 契约,internal 墙由编译器强制
PostgreSQL 优先,MySQL 同样支持,迁移分目录维护
两种数据库上均通过,覆盖主要教学链路
含课件 121、班级 10、用户 40,ID 全部保留
仓库里数得出来的事实
下面每一项都能在 unile-go 里核对:模块、数据表、接口与能力项。我们判断一件事「做完没有」的方式,就是看这些数字有没有变。
安全与合规支撑点
Unilearning 在安全上具体做了什么、边界画在哪里。每一条都标注了对应的实现位置,方便核对。
学员取卷接口不返回 answer / analysis,答案不下发到客户端。
quiz 域取卷接口跨域只能 import 别人的 api 契约,越界直接编译失败,不靠文档约束。
Go internal 机制local 模式内建 bcrypt + JWT;切 remote 即对接 UIAM,其余域代码不动。
auth 域 local / remote每域一组方言迁移文件,版本记录在库内,已应用自动跳过,失败可回退。
unile_migrations 表OSS STS 凭证接口支持真实 AssumeRole,避免长期密钥硬编码。
OSS STS 接口三端路由(uv1 / tv1 / admv1)认证已全部补齐,旧版部分路由无认证。
edge 聚合层数据停在哪一层
数据边界由产品形态决定,不是一个可以随手打开的开关。这是四条业务线共同的判断。
端侧产品的最小边界。这一层的数据在设计上就没有上传通道,不是靠开关关闭的。
- legdger 全部账目:本机加密存储,云端只有密文
- legdger 端侧 AI 统计与问答:推理在设备内完成
- NewTool 桌面与 wasm 形态:算法内核无 IO,数据不出进程
- pxc 抓包流量:内核跑在本机,不经第三方服务
私有化交付的边界。模型、向量、业务数据与审计记录全部部署在客户自己的网络里。
- Uniclaw 记忆与知识向量:自建 Milvus,不出内网
- Uniclaw 模型服务:可对接私有化部署的兼容协议服务
- UIAM 身份与审计数据:整套部署在客户网络边界内,可完全离网
- Uniscrm 素材与媒体:对象存储可对接客户自有 OSS
- Unilearning 课件与学习记录:单容器交付,数据自主
唯一需要外部网络的是同步与外部通道,且传输内容是加密密文或已脱敏的消息。
- legdger 跨设备同步:只上传最新密文,服务端不积历史
- Uniscrm 企微通道:与企业微信官方接口通信,走官方存档能力
- Uniclaw 通道:飞书 / 钉钉 push API,内容按租户隔离
- NewTool 远程调用:经 MCP 传输,算法内核本身不发起网络请求
公司级安全原则
不管哪条业务线,这六条是共同的底线。
凡是能写死在编译器、框架或查询层里的约束,就不写进文档让人记住。租户隔离由查询层监听器强制,域边界由 Go 的 internal 机制在编译期拒绝。
jOOQ TenantScopeVisitListener · Go internal 墙敏感数据默认留在用户设备或客户网络内。四条业务线全部支持私有化交付,端侧产品(legdger)连 AI 分析都不出设备。
legdger 端侧 AI · Uniclaw 自建 Milvus · Unilearning 单容器Agent 不共用人类凭据,服务端不硬编码长期密钥,端侧接入走可撤销令牌。任何凭据都能被单独吊销而不影响其他主体。
UIAM MACHINE 主体 · OSS STS AssumeRole · ledger-cli 令牌认证、授权、工具调用、抓包调试全部产生结构化记录。不是为了合规而记,是为了出事时能回放。
uiam-audit · collaboration-logger · pxc 会话记录委托关系用 act claim 表达「谁代表谁」,权限逐跳取交集。审计里能回答「哪个用户授权了哪个 Agent、调用了哪个工具、用了哪些权限」。
RFC 8693 Token Exchange · agent_tools 注册表数据库变更走版本化迁移,重复执行结果一致;应用发布是单镜像替换,回滚就是换回上一个镜像。
Liquibase · 各域方言迁移 · Docker 单镜像部署与集成
全部产品线均支持私有化交付,具体形态因产品而异。
1 个二进制、1 个容器,十余个容器变 1 个。
- make docker && docker run -p 8080:8080
- GOWORK=off 靠各 go.mod 的 replace 解析
- 启动时自动执行各域版本化迁移
PostgreSQL 优先,MySQL 同样受支持。
- config.yaml 里 driver 二选一
- 迁移按 pg / mysql 分目录维护
- 17 项端到端冒烟在两种库上均通过
从 6 个旧微服务平滑切换,前端不用改。
- uv1 / tv1 / admv1 路由路径保持前端兼容
- legacy-import 导入旧库,ID 全部保留
- 老用户 bcrypt 原密码直接登录
可以独立运行,也可以交给 UIAM。
- local 模式:内建 bcrypt + JWT,零外部依赖
- remote 模式:替换 auth 实现即可对接 UIAM,其余域不动
- 三端路由认证已全部补齐(旧版部分路由无认证)
方案对比
同一件事,不同的做法。左栏是我们的选择,右栏是常见的替代方案——差别通常不在功能表上,而在边界画在哪里。
谁会感觉到变化
功能清单说服不了人,角色视角可以。下面是四类角色接入前后的真实差别。
培训做完了,不知道谁真的学完、谁在哪个环节卡住
学习进度沿大纲树自动汇总,完成率与卡点实时可查
一个班几十份作业,主观题批到深夜,还容易漏
AI 生成批改草稿,教师只做终审拍板,重复劳动大幅下降
视频看到一半断了,下次回来找不到位置,进度也对不上
四类课件统一进度口径,断点续学,大纲视图实时同步
微服务一堆容器,改一处要滚动升级整个集群
单容器交付,启动自动幂等迁移,回滚就是换回上一个镜像
版本与路线
已发布的写清楚交付了什么,开发中的写清楚正在做什么,规划中的写清楚打算做什么。已发布的条目不会再被推翻。
v1.0已发布教学内核- 8 个业务域模块化单体
- 课程树与学习记录冒泡
- 四类课件与断点续学
- 题库组卷与交卷即判分
v1.1已发布AI 与交易- 单份 / 整班 AI 批改
- 在线商城与订单状态机
- 盖楼式评论社区
- 旧系统 legacy-import
v1.3开发中学习体验- 学习路径推荐
- 证书与学分体系
- 直播课件形态
- 移动端离线学习
v2.0规划中多租户- 多机构多租户隔离
- 开放 API 与插件生态
- 学习数据分析看板
- 与 UIAM 完整联邦
集成与对接对象
Unilearning 需要和谁打交道、用什么方式。
首选方言,迁移按 pg 目录维护,17 项冒烟全通过
同等支持,业务查询保持双方言兼容
纯标准库实现,GLM / DeepSeek / 本地 Ollama 均可
remote 模式替换 auth 实现即完成对接,其余域不动
课件与媒体资源,支持真实 AssumeRole 临时凭证
旧库与旧身份库一键导入,ID 保留,原密码可用
落地场景
这套东西在真实业务里怎么用。
新员工培训、合规考试、岗位认证一条链路。
- 课程树组织培训大纲
- 题库考试做合规考核
- 学习进度看板追踪完成率
课程要卖,学员要管,老师要批作业。
- osm 商城完成课程售卖闭环
- 班级与排课管理线下/线上班次
- AI 批改降低教师重复劳动
一个人也能跑起来的课程生意。
- 单容器部署,运维成本极低
- 商城与订单开箱可用
- 评论社区沉淀学员互动
已经在跑的老平台,不想推倒重来。
- 前端路由路径保持兼容
- 老数据一键导入,原密码可用
- 迁移分域推进,风险可控
常见问题
旧系统 6 个微服务靠 HTTP + URL 配置互相调用,真正的成本在服务间调用与部署复杂度。模块化单体把调用变回进程内函数调用,同时用 Go 的 internal 机制让跨域 import 直接编译失败——边界由编译器守住,而不是靠 lint 自觉。
把身份、Agent 与私域的复杂度,交给一套可治理的内核
无论你是要替换现有的 IAM、搭建 Agent 中台,还是想把企微私域真正运营起来——先从一次 30 分钟的架构沟通开始。我们会先判断问题是不是我们擅长解的那类,不合适会直接说。