计算机学习与求职成长一体化平台
从刷题学习,到求职上岸
在线判题 · 面试题库 · 班级作业 · 问答社区 · 教程课程 · 打卡签到 · AI 助手
码途(matu)是一个面向计算机专业学生与开发者的学习社区平台。它把「学、练、问、评」串成一条完整的链路:用教程与课程讲知识,用在线判题即时反馈,用面试题库备岗,用班级作业与排名支撑教学,用问答社区沉淀经验,再用 AI 助手降低学习与创作的启动成本。
项目采用四端协同 + 微服务后端的结构:Web 用户端供学习者使用,管理后台供运营维护内容,微信小程序与移动 App 覆盖移动场景;后端按业务域拆分为 14 个微服务,统一经网关鉴权,配套消息队列、缓存、搜索引擎与容器化判题沙箱。
| 模块 | 说明 |
|---|---|
| 在线判题 OJ | 多语言代码编辑器(Monaco),提交判题、样例对比、提交记录与题解,Docker 沙箱隔离执行 |
| 面试题库 | 按分类与难度整理的面试题,支持 VIP 解锁、收藏与学习进度记录 |
| 班级与作业 | 班级创建与成员审核、班级题目池、作业布置与截止时间,提交后自动统计排名 |
| 竞赛 | 按 ICPC 罚时赛制生成竞赛排行榜,输出逐题网格榜 |
| 问答社区 | 提问、回答、采纳与评论,配合搜索与标签定位问题 |
| 教程与课程 | 教程文档与视频课程,章节学习与学习进度追踪 |
| 文章与创作 | Markdown 编辑器、封面与分类标签、一键发布到首页信息流,支持多人协同编辑 |
| 打卡签到 | 每日打卡、连续天数与最长连击统计、成就体系与补签 |
| 消息与私信 | 站内通知、点赞评论提醒与一对一私信(WebSocket 实时推送) |
| AI 助手 | 基于工具调用(MCP)的智能问答、文章摘要与内容生成,流式输出 |
| 会员与支付 | 会员权益、支付宝扫码/网页支付与订单管理 |
| 统一搜索 | Elasticsearch 全站检索文章、课程、面试题与题目 |
| 文件服务 | 对象存储分片上传、断点续传与 MD5 秒传 |
| 用户与权限 | 注册登录、RBAC 角色、企业/学校/身份认证 |
| 层次 | 技术选型 |
|---|---|
| 用户端 Web(matu-user) | React 19 · TypeScript · Ant Design 6 · Redux Toolkit · React Router 7 · Vite · Monaco Editor |
| 管理后台(matu_admin) | Vue 3 · Element Plus · Pinia · Vue Router · Vite · Sass |
| 微信小程序(matu-liteapp) | uni-app |
| 移动 App(matu-appp) | Flutter · GetX |
| 后端(matu-backend) | Java 17 · Spring Boot 3 · Spring Cloud(Nacos / Gateway / OpenFeign)· MyBatis-Plus · Flyway |
| 数据与中间件 | MySQL 8(12 个业务库)· Redis · RabbitMQ · Elasticsearch 8 · PostgreSQL + pgvector(AI 向量检索) |
| 判题沙箱(matu-code-sandbox) | Docker · openjdk / python / gcc 镜像,容器级资源隔离 |
┌────────────────────────── 客户端 ──────────────────────────┐
│ 用户端 Web 管理后台 微信小程序 移动 App │
└───────────────────────────┬───────────────────────────────┘
│ HTTP · WebSocket · SSE
┌───────▼────────┐
│ Gateway │ Sa-Token 鉴权 · HMAC 身份签名 · 限流
└───────┬────────┘
│ OpenFeign(内部令牌)
┌────────┬────────┬─────┴─────┬────────┬────────┬────────┐
auth post oj message search pay ai ...
└────────┴────────┴───────────┴────────┴────────┴────────┘
│
MySQL · Redis · RabbitMQ · Elasticsearch · 对象存储 · Docker 判题沙箱
matu/
├── matu-user/ 用户端 Web(React + TypeScript + Ant Design)
├── matu_admin/ 管理后台(Vue 3 + Element Plus)
├── matu-liteapp/ 微信小程序(uni-app)
├── matu-appp/ 移动 App(Flutter + GetX)
├── matu-backend/ 后端微服务(Spring Cloud)
│ ├── gateway/ 统一网关:鉴权、身份签名、限流
│ ├── common/ 公共模块:通用返回、安全、内部契约
│ ├── service/ 14 个业务微服务
│ │ ├── service-auth/ 认证、用户、角色与会员
│ │ ├── service-post/ 文章、动态、点赞与评论
│ │ ├── service-oj/ 题库、提交与判题调度
│ │ ├── service-check/ 打卡、统计与成就
│ │ ├── service-message/ 私信、通知与协同编辑
│ │ ├── service-search/ Elasticsearch 统一搜索
│ │ ├── service-pay/ 支付宝支付与订单
│ │ ├── service-ai/ AI Agent、RAG 与异步任务
│ │ ├── service-mcp/ MCP 工具服务
│ │ └── ... 课程、面试、问答、文件、信息
│ └── database/ 数据库初始化脚本(bootstrap / seeds)
├── matu-code-sandbox/ 代码判题沙箱(Docker)
└── nacos-configs/ Nacos 服务配置模板
从 AI Agent 到支付交易、统一搜索、消息流转,挑十几个最能体现工程深度的子系统展开讲讲。
自研 OpenAI 兼容客户端 · 多轮 Function Calling · MCP 工具协议 · 流式输出
- 不绑定厂商:自研
OpenAiCompatibleClient(WebClient + SSE 流式),Embedding 兼容 OpenAI 与 DashScope 两套响应格式,换模型只改配置。 - 多轮工具循环:
AiAgentService解析tool_calls→ 执行工具 → 回填结果 → 递归,由ai.agent.max-tool-calls(默认 4)封顶,避免无限调用。 - 工具即服务:工具后端是独立的 service-mcp,JSON-RPC 2.0 分发(
McpDispatcher);内置课程检索、OJ 题目与提交查询、联网搜索、网页抓取(含 SSRF 防护)。 - 上下文与检索:Redis List
ai:memory:{userId}:{conversationId}存近期对话(约 20 条 / 30 天),MySQL 落库持久化;pgvectorai_knowledge做向量检索(1024 维,topK 5 / 阈值 0.7),检索异常静默降级。 - 安全治理:
PromptGuard提示注入过滤、AiRateLimiter限流(20 次/分、并发 2),重任务走 MQ 异步并配死信队列。
Function Calling · MCP / JSON-RPC · SSE 流式 · pgvector RAG · 限流与降级
Redis 原子脚本 · 三级缓存 · 版本号 CAS · Outbox + 对账
- 写路径全原子:单条 Lua 脚本在 Redis 内一次性完成「幂等判定 + 状态翻转 + 版本号 +1 + 写入待发队列」,天然免疫并发重复点赞。
- 三级读取:Caffeine 本地缓存 → Redis(权威数据源)→ MySQL 兜底;本地缓存带随机抖动过期防雪崩,DB 兜底用信号量限 8 并发保护数据库。
- 版本号 CAS:每次变更携带
version,落后版本返回STALE,保证「新操作绝不被失败的旧操作回滚」。 - 可靠投递:Redis Hash 当 outbox,定时任务补偿发 MQ,成功才清理;另有一份每日 2 点对账任务扫描 Redis 与 MySQL 差异并自动修复。
- 热 key 探测:用 HeavyKeeper(重击者算法)统计访问频次,超过阈值才升级到本地缓存;所有键用 hash tag
post:like:{postId}:*保证同槽位、适配 Redis Cluster。
Redis Lua · Caffeine L1 · 版本号 CAS · Outbox · 对账补偿 · 热 key 探测
RabbitMQ 异步流水线 · 容器级隔离 · 资源限额 · 死信兜底
- 异步解耦:提交先落库为
WAITING,事务提交后(afterCommit)才投递oj.judge.queue;消费者手动 ack、prefetch=1,配合 Redis 锁oj:judge:lock:{id}防重复消费。 - 失败兜底:应用内重试 3 次,耗尽后
basicNack(requeue=false)经死信交换机oj.judge.dlx落入oj.judge.dlq,不阻塞主队列。 - 容器隔离:独立沙箱服务(docker-java)常驻容器 + exec 执行;关闭网络、只读根文件系统、tmpfs
/tmp限额 64M、CPU 1 核、内存上限且交换分区为 0。 - 多语言与限额:
openjdk:8-alpine/python:3.9-alpine/gcc:12.2.0;编译限 15s,运行默认 1000ms,退出码 137 判定为内存超限。 - 状态机:8 态枚举(WAITING / AC / WA / RE / TLE / MLE / CE / SYSTEM_ERROR),逐样例比对,全部通过才判 AC。
RabbitMQ · 死信队列 · Docker 沙箱 · cgroups 限额 · Redis 幂等锁
Operational Transform · WebSocket 房间广播 · LMAX Disruptor
- 自研 OT(非 CRDT):把编辑抽象为
insert/delete的 TextOperation,由变换器做冲突合并(含重叠删除),服务端保存操作日志而非整篇覆盖。 - 定序与幂等:提交以客户端
baseRevision为基准,与之后已提交的操作逐一 transform 后再应用,全局 revision + 1;(clientId, requestId)去重,重发请求直接返回原结果。 - 实时通道:与私信共用 WebSocket 端点,握手统一走 Sa-Token 鉴权;房间内广播加入/离开/光标/正在输入,并把操作实时下发到各端。
- 前端协同:编辑器本地 diff 出操作增量后经 WebSocket 上行,在线光标与选区随操作一同同步,实现「同文档多人同时编辑」。
- 削峰异步:操作提交流水线接入 LMAX Disruptor 环形队列,把高频编辑从请求线程卸下来,避免阻塞。
Operational Transform · WebSocket · Sa-Token 鉴权 · LMAX Disruptor · 光标共享
官方 SDK · RSA2 验签 · 回调幂等 · 权益发放
- 官方 SDK 接入:独立服务 service-pay(独立库),用 alipay-sdk-java 的
DefaultAlipayClient,支持扫码(Precreate)与网页(PagePay)两种下单方式。 - 回调闭环:支付宝异步通知
POST /pay/alipay/notify→ 全量验签并校验app_id与金额一致 → 更新订单与流水 → Feign 调用 service-auth 发放会员权益。 - 双重幂等:通知日志按
notify_id唯一去重;权益发放用activation_key = "pay:" + 订单号唯一键 + insertIgnore,重复通知不会重复发权益。 - 并发串行化:订单与支付流水更新走
SELECT ... FOR UPDATE行锁,避免同一订单被并发改单。 - 状态机:
OrderStatus(待支付 / 已支付 / 已关闭 / 已退款 / 部分退款)流转,下单与拉起支付时惰性判断 30 分钟支付截止时间并关闭超时单。
支付宝 SDK · RSA2 验签 · 回调幂等 · 行锁 · Feign 发权益
官方 Java API Client · 多字段加权 · 别名原子切换
- 独立搜索服务:service-search 用官方 elasticsearch-java 客户端,统一检索文章 / 课程 / 面试题 / OJ 题目四类内容,type 区分、
type=all时聚合返回。 - 字段加权检索:multiMatch 对标题、摘要、标签、分类做加权匹配(如标题权重 4、摘要权重 2),配合类型与状态过滤、深分页上限保护。
- 零停机重建:同步时写入带时间戳的临时索引,bulk 导入完成后原子切换别名,失败即删除新索引——查询全程不中断。
- 数据来源安全:各业务服务通过
/internal/search-documents内部端点提供文档(游标分页推进),带共享令牌校验;网关对内部端点直接返回 404,杜绝公网直连。 - 触发方式:定时自动重建(默认每 30 分钟)或管理端一键触发,重建期间旧索引照常服务。
Elasticsearch 8 · multiMatch 加权 · 别名原子切换 · 内部令牌
4 组交换机 · 手动 ack · 死信兜底 · Outbox 补偿
- 统一拓扑:4 组直连交换机各配死信队列,覆盖点赞、AI 文章摘要、AI 异步任务与 OJ 判题(如判题链路
oj.judge.exchange → oj.judge.queue,失败进oj.judge.dlq)。 - 可靠消费:手动 ack、prefetch 限流、应用内重试 3 次,耗尽后
basicNack(requeue=false)落入死信队列,不再反复入队阻塞主链路。 - 事务后投递:判题消息在事务
afterCommit回调里才发送,从根上避免「消息已发出但事务回滚」的脏消息。 - Outbox 兜底:数据库本地消息表 + 定时补投(AI 摘要),点赞侧用 Redis Hash 当 outbox;均等发布确认成功后才清理记录。
- 跨服务事件:文章发布事件由内容服务投递到 AI 队列、由 AI 服务消费生成摘要,再经 HTTP 回写——典型的服务解耦与异步化。
RabbitMQ · 手动 ack · 死信队列 · Outbox · 发布确认
Sa-Token · HMAC 身份签名 · 内部端点隔离
- 集中式鉴权:网关用 Sa-Token 统一拦截,登录、注册、支付回调等白名单之外一律校验 token,业务服务不再各自重复鉴权。
- 身份签名防伪造:网关校验 token 后签发用户 ID / 角色 / 时间戳,并用 HmacSHA256 签名;下游验签 + 30 秒时间戳偏移校验 + 常量时间比较,同时清除伪造的转发头,构成零信任边界。
- 内部端点隔离:内部同步接口在网关侧强制登录或直接返回 404;服务间调用另带共享令牌(支付、搜索、AI 各有独立令牌)。
- 网关限流与名单:敏感接口用 Redis Lua 固定窗口限流,另有 IP 黑白名单过滤器。
- 会话与密码:同端登录可顶下线;密码用 BCrypt(强度 12)存储,历史 SHA-256 账号登录成功后透明升级。
Sa-Token · HmacSHA256 · 内部令牌 · Lua 限流 · BCrypt
对象存储分片 · 断点续传 · MD5 去重
- 分片上传:走对象存储的「初始化 → 上传分片 → 完成」三段式,用分片记录表保存已传进度,支持大文件断点续传。
- 秒传:以文件 MD5 + 大小做去重,命中已存在文件直接返回句柄,跳过整个上传过程。
- 僵尸清理:定时任务定期中止长期未完成的分片上传,回收存储空间与未完成配额。
- 上传安全:服务端按白名单判定内容类型,防止把 HTML 伪装成图片造成存储型 XSS;私有文件下载走签名 URL。
OSS 分片上传 · 断点续传 · MD5 秒传 · Content-Type 白名单
真实赛制算法 · 网格榜输出
- 赛制规则:按「解题数 > 罚时 > 最后 AC 时间」排序;罚时 = AC 用时 + 错误提交数 × 20 分钟,基准时间取竞赛开始或最早提交,贴近真实 ICPC 规则。
- 网格榜:返回逐题状态的网格数据(每题是否通过、该题尝试次数),前端可直接渲染成竞赛榜单。
- 范围约束:只统计竞赛时间窗内、竞赛题目池内的提交,避免场外提交刷榜。
ICPC 罚时 · 排行榜 · 竞赛时间窗
增量统计 · 连击计算 · 成就体系 · 补签
- 连续天数:扫描日历计算当前连续与历史最长连续打卡天数,跨月也能正确衔接。
- 增量统计:按月的统计表增量刷新,避免每次心跳都全量重算,排行榜与个人页读取更快。
- 成就体系:按累计打卡天数、发布内容数、最长连击等多维度判定成就并实时刷新。
- 补签:支持补签(限一年内),补签后自动重建当月的统计与连续天数。
连续天数 · 增量统计表 · 成就判定 · 补签
网关 + 服务双配额 · 阶梯封禁 · Sentinel 热更新
- 第一层 · 网关限流:Redis Lua 固定窗口限流,拦住粗粒度的高频流量。
- 第二层 · 服务内双配额:Lua 脚本同时维护短期与 24 小时两级配额,超限后阶梯封禁(多次触发则封禁时长从分钟级升到小时级)。
- 第三层 · 动态熔断:Sentinel 的 QPS 流控与响应时间降级规则由 Nacos 下发并热更新;配置非法时回退到上一次生效的规则,避免改错配置反而打穿保护。
- 效果:爬取、刷接口等异常流量在三层拦截下逐级衰减,正常用户几乎无感。
Lua 固定窗口 · 双配额 · 阶梯封禁 · Sentinel + Nacos
贯穿各模块的通用设计取舍
- 服务治理:Nacos 注册与配置中心 + Gateway 统一入口,服务间用 OpenFeign 内网调用并带内部密钥头,超时可控。
- 数据访问:MyBatis-Plus + 游标分页 + 批量操作;数据库变更用 Flyway 版本化迁移,改表前做列存在性守卫保证幂等可重复执行。
- 权限模型:RBAC 角色(普通用户 / 管理员 / 讲师 / 会员),网关与服务内双重校验,权限绝不信任前端。
- 实时能力分层:AI 问答走 SSE 流式,协同与私信走 WebSocket,判题、摘要、点赞走 MQ——按场景选型而非一栈到底。
- 安全细节:网页抓取工具做 SSRF 防护(拦截环回、内网与云元数据地址),AI 侧有提示注入词表过滤,文件上传有内容类型白名单。
- 可观测与容错:关键链路带日志埋点;缓存、检索、下游服务异常均有降级路径,单点故障不拖垮主流程。
- JDK 17、Maven 3.9+
- Node.js 18+(Web 端与小程序)
- Flutter 3.x(移动 App)
- MySQL 8、Redis、RabbitMQ、Elasticsearch 8、Nacos 2.x
- Docker(判题沙箱)
- 可选:PostgreSQL + pgvector(AI 向量检索)、对象存储(文件服务)、支付宝账号(支付)、大模型 API(AI)
项目按服务拆分为 12 个 MySQL 业务库。为每个库创建空库(字符集 utf8mb4)后导入对应脚本:
CREATE DATABASE matu_info CHARACTER SET utf8mb4;
USE matu_info;
SOURCE matu-backend/database/bootstrap/matu_info.sql;对 matu-backend/database/bootstrap/ 下的 12 个文件重复上述步骤,再在 matu_auth 库执行 seeds/matu_auth.sql 初始化角色。脚本不含任何用户、订单与业务数据,也不含默认账号密码。
- 复制根目录的
.env.example为.env,填入你自己的数据库连接等信息; - 将
nacos-configs/下的配置导入 Nacos(分组nacos),并填入你自己的 Redis、RabbitMQ、Elasticsearch、对象存储与模型服务凭据。
切勿把真实密钥提交到公开仓库。仓库内的配置均为模板,请通过环境变量或本地未跟踪文件注入。
cd matu-backend
mvn -s settings.xml -pl service/service-auth -am -DskipTests package
# 按需启动其余服务与网关;首次启动由 Flyway 自动执行版本迁移建表cd matu-code-sandbox
mvn -DskipTests package
# 构建镜像并启动服务(默认端口 8091)
# 需通过环境变量 SANDBOX_AUTH_SECRET 注入调用方共享密钥# 用户端 Web —— http://localhost:5173
cd matu-user && npm install && npm run dev
# 管理后台 —— http://localhost:5174
cd matu_admin && npm install && npm run dev
# 微信小程序:构建后用微信开发者工具导入产物目录
cd matu-liteapp && npm install && npm run build:mp-weixin
# 移动 App
cd matu-appp && flutter runpython matu-backend/scripts/smoke_public.py --base-url http://127.0.0.1:8080本项目采用 Apache License 2.0 开源协议。你可以自由使用、修改与分发本项目,包括用于商业用途;使用时请保留原始版权与许可声明。协议同时包含明确的专利授权条款。
移动端 App 基于 FlutterKit 模板开发,其上游代码以 MIT 协议授权,署名声明见 matu-appp/LICENSE-FlutterKit。
欢迎提交 Issue 反馈问题,或通过 Pull Request 提交改进。提交前请确保:
- 不包含任何真实密钥、账号、订单或个人数据;
- 变更与现有代码风格保持一致;
- 相关模块能够正常构建。
本项目的完成离不开开源社区。谨向所有被使用的开源项目及其贡献者致以诚挚谢意。