项目背景

一个基于 Spring Boot 4.0.4 + MyBatis-Plus + PostgreSQL + Redis + Vue 3 的 C 语言作业批改与教学管理平台。功能很完整——AI 批改、知识库 RAG、热力图分析、QQ Bot 通知、教案生成——但离生产级差距不小。

初始评估:4.0 / 10.0


一、安全加固

1.1 JWT Token 泄露到日志

JwtAuthenticationFilter.java 中三处把 token 完整或部分打印到了日志:

1
2
3
4
5
// 坏味道:完整打印 token
log.info("提取的token: {}", token);

// 坏味道:打印 token 前 20 字符
log.warn("Token验证失败,token: {}", token.substring(0, 20) + "...");

修复:日志中只记录 path 和方法名,永远不输出 token 内容。同时发现该文件错误 import 了 reactor.netty.http.HttpConnectionLiveness.log,改用 Lombok 的 @Slf4j

1.2 密钥硬编码

application.properties 中数据库密码、JWT Secret、OpenClaw API Key、OneBot Token 全部有弱默认值:

1
2
3
4
# 修复前
spring.datasource.password=${DB_PASS:123456}
jwt.secret=${JWT_SECRET:dev-only-jwt-key-do-not-use-in-production-32bytes}
langchain4j.openai.api-key=${OPENCLAW_API_KEY:change-it-before-production}

修复:移除所有 fallback 默认值,改为 ${VAR} 无默认值。缺少环境变量启动即报错(fail fast)。本地开发值放入 application-local.properties(已 gitignore)。docker-compose.yml 密码改为 ${DB_PASS:?请设置 DB_PASS}


二、高可用与容错

2.1 AI 服务熔断

所有 AI 调用经过 OpenClawServiceImpl,没有任何保护。AI 服务挂了会导致请求堆积、线程耗尽。

修复:引入 Resilience4j,对 chat()streamChat()@CircuitBreaker

1
2
3
4
5
6
7
8
@CircuitBreaker(name = "openclaw", fallbackMethod = "chatFallback")
public String chat(String message, String sessionId, String status) { ... }

// 熔断打开时:聊天返回友好提示,批改触发重试机制
private String chatFallback(String msg, String sid, String st, Throwable t) {
log.warn("AI 服务熔断/降级: {}", t.getMessage());
throw new BusinessException(ErrorCode.AI_SERVICE_ERROR);
}

熔断配置:滑动窗口 10 次调用,>50% 失败自动熔断,30 秒后半开试探。

2.2 单点故障消除

组件 原状态 修复
Redis 无持久化,重启丢队列 AOF + RDB 双持久化
PostgreSQL 无备份 pgbackup 容器每日 pg_dump,保留 7 天
App 单实例 Nginx 负载均衡 + --scale app=2
App 关闭 直接 kill server.shutdown=graceful,等待 30s
无健康检查 Actuator + Docker healthcheck

2.3 Nginx 负载均衡

1
2
3
4
5
6
7
8
upstream backend {
server app:8080 max_fails=3 fail_timeout=30s;
}

location /api/ {
proxy_pass http://backend;
proxy_read_timeout 300s;
}

启动多副本:docker-compose up -d --scale app=2


三、数据管理

3.1 Flyway 数据库迁移

项目中已经有 Flyway 命名的迁移文件(V2~V12),但根本没装 Flyway,全靠手工执行。

修复

  • 添加 flyway-core + flyway-database-postgresql 依赖
  • schema.sqlV1__init_schema.sql,整理冲突文件(V7 重复、V20250510 非标准命名)
  • 已有数据库设 baseline-on-migrate=true + baseline-version=14,新库跑全量迁移
  • 移除 docker-compose 中的 schema.sql 挂载

3.2 N+1 查询优化

DashboardServiceImpl.getClassList() — 查 N 个班级,逐条查学生数:

1
2
3
4
5
6
7
8
9
10
-- 修复前:1 + N 次查询
SELECT * FROM class_info WHERE teacher_id = ?;
SELECT COUNT(*) FROM sys_user WHERE class_id = ?; -- ×N

-- 修复后:1 次 JOIN
SELECT ci.*, COUNT(u.id) as student_count
FROM class_info ci
LEFT JOIN sys_user u ON u.class_id = ci.id AND u.status = 1
WHERE ci.teacher_id = ?
GROUP BY ci.id;

saveTeacherKnowledge() 逐条 insert 改为批量 insertBatch()


四、文件存储

从本地磁盘升级为 S3 标准协议(AWS SDK v2),一套代码兼容所有对象存储:

1
2
3
4
storage.type=local → 本地磁盘(开发)
storage.type=s3 → MinIO(docker-compose 自带)
→ 阿里云 OSS(改 endpoint)
→ AWS S3 / 腾讯云 COS

切生产环境只需改环境变量:

1
2
3
4
5
STORAGE_TYPE=s3
S3_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
S3_BUCKET=firedemo-prod
S3_ACCESS_KEY=xxx
S3_SECRET_KEY=xxx

代码零改动。


五、代码质量

5.1 状态魔法值 → 枚举

11 处 "COMPLETED""FAILED""PENDING""PROCESSING" 字符串统一定义为 SubmissionStatus 枚举:

1
2
3
4
5
6
7
8
9
10
11
@Getter
public enum SubmissionStatus {
PENDING("PENDING", "排队中"),
PROCESSING("PROCESSING", "批改中"),
COMPLETED("COMPLETED", "已完成"),
FAILED("FAILED", "失败");

public static boolean isFinal(String code) {
return COMPLETED.code.equals(code) || FAILED.code.equals(code);
}
}

5.2 JSON 提取去重

项目中 3 套 JSON 提取逻辑(ChatController.extractJsonFromMarkdownStructuredOutputInvoker.extractJsonDashboardServiceImpl.extractFirstJson)合并为一个 JsonUtil.extractJson()

5.3 Bucket4j API 升级

1
2
3
4
5
6
7
8
9
// 旧(已过时)
Refill refill = Refill.greedy(rate, Duration.ofMinutes(1));
Bandwidth limit = Bandwidth.classic(capacity, refill);

// 新
Bandwidth limit = Bandwidth.builder()
.capacity(capacity)
.refillGreedy(rate, Duration.ofMinutes(1))
.build();

六、前端优化

6.1 Auth Store 修复

stores/auth.tslogin()register() 完全不调 API——用邮箱前缀构造假用户。改为通过 request.ts(axios)调真实后端接口。

6.2 API 调用统一

项目中有两套 API 机制混用

  • axios(request.ts)——少数组件在用
  • raw fetch() —— 6 个组件硬编码 http://localhost:8080/api

统一为 axios,好处:

  • 全局 baseURL 管控
  • 自动带 token
  • 401 自动跳转登录
  • 统一错误拦截

修复了 LoginViewRegisterViewSubmissionViewTaskDetailTaskManageKnowledgeBase 共 6 个文件。

6.3 Markdown 渲染

AI 聊天页面从 1196 行缩到 310 行,手写的正则 Markdown 解析替换为 marked + highlight.js

1
2
3
4
5
6
marked.use(markedHighlight({
highlight(code: string, lang: string) {
if (lang && hljs.getLanguage(lang)) return hljs.highlight(code, { language: lang }).value
return hljs.highlightAuto(code).value
}
}))

AI 回复现在支持代码高亮、表格、引用、列表等完整 Markdown 语法。

6.4 文件命名规范化

1
2
3
4
5
PageOne.vue    →  StudentSubmit.vue
PageTwo.vue → AIChat.vue
PageThree.vue → KnowledgeBase.vue
PageFour.vue → Dashboard.vue
PageFive.vue → TaskManage.vue

成果

1
2
3
4
5
6
7
8
9
10
11
12
维度          优化前   优化后
──────────────────────────
安全性 ⭐⭐ ⭐⭐⭐⭐
高可用 ⭐⭐ ⭐⭐⭐⭐
可观测性 ⭐⭐ ⭐⭐⭐
数据管理 ⭐⭐ ⭐⭐⭐⭐
性能 ⭐⭐⭐ ⭐⭐⭐⭐
代码质量 ⭐⭐⭐ ⭐⭐⭐⭐
运维成熟度 ⭐⭐⭐ ⭐⭐⭐⭐
测试 ⭐ ⭐
──────────────────────────
总分 4.0 6.5

改动统计

类别 数量
新增文件 12 个
修改文件 28 个
删除文件 3 个
新增依赖 6 个

下一步

测试覆盖率从 0 提升到 40%+,引入 CI/CD(GitHub Actions),补 Metrics + Tracing,可到 7.5~8.0 分。


生产级不是一蹴而就的——它是安全、高可用、可观测、代码规范、持续交付这些横切关注点的系统化积累。