前言

今天为作业批改系统增加了QQ机器人提醒功能,包括作业截止提醒、成绩不理想私聊提醒等。记录一下实现过程和遇到的问题。

功能需求

  1. 作业截止前群提醒 - 在截止前24小时和1小时发送群消息
  2. 成绩不理想私聊提醒 - 分数<60分时私聊学生
  3. 首次提交绑定QQ号 - 收集学生QQ号用于后续提醒

架构设计

1
2
3
4
5
┌─────────────┐     HTTP      ┌─────────────┐
│ 后端服务 │◄────────────►│ Napcat │
│ (Spring │ OneBot协议 │ (QQ机器人) │
│ Boot) │ │ │
└─────────────┘ └─────────────┘

选择直接调用 Napcat HTTP 接口,而不是通过 OpenClaw Gateway,这样可以:

  • 降低延迟
  • 代码逻辑更清晰
  • AI对话和消息发送解耦

核心实现

1. OneBot HTTP 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Service
@RequiredArgsConstructor
public class OneBotHttpService {

private final OneBotProperties properties;
private final WebClient webClient = WebClient.create();

public void sendPrivateMessage(String qqNumber, String message) {
Map<String, Object> body = Map.of(
"user_id", qqNumber,
"message", message
);
sendRequest("/send_private_msg", body);
}

public void sendGroupMessage(String groupId, String message) {
Map<String, Object> body = Map.of(
"group_id", groupId,
"message", message
);
sendRequest("/send_group_msg", body);
}
}

2. 学生名单自动收集

采用渐进式策略:

  • 首次作业:学生提交时自动收集到 class_student
  • 后续作业:可以提醒未交学生
1
2
// 学生提交作业后自动加入班级名单
classStudentMapper.insertIgnore(classId, studentId, studentName, "auto");

3. 定时提醒调度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class TaskReminderScheduleService {

public void scheduleReminderTasks(Long taskId) {
HomeworkTask task = taskMapper.selectById(taskId);

// 24小时前提醒
LocalDateTime remind24h = task.getDeadline().minusHours(24);
scheduleTask(taskId, remind24h, 24);

// 1小时前提醒
LocalDateTime remind1h = task.getDeadline().minusHours(1);
scheduleTask(taskId, remind1h, 1);
}
}

数据库设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- 学生QQ绑定表
CREATE TABLE student_qq_binding (
student_id VARCHAR(32) PRIMARY KEY,
qq_number VARCHAR(32) NOT NULL,
student_name VARCHAR(64),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 班级学生表(自动收集)
CREATE TABLE class_student (
id BIGSERIAL PRIMARY KEY,
class_id BIGINT NOT NULL,
student_id VARCHAR(32) NOT NULL,
student_name VARCHAR(64) NOT NULL,
source VARCHAR(20) DEFAULT 'auto', -- auto/manual
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(class_id, student_id)
);

前端交互

首次提交时弹出QQ绑定对话框:

1
2
3
4
5
6
7
8
<div v-if="showQqBindDialog" class="dialog-overlay">
<div class="dialog-content">
<h3>🔔 首次提交,请绑定QQ号</h3>
<p>绑定后,当作业成绩不理想时,我们会通过QQ私聊提醒你。</p>
<input v-model="qqNumberInput" placeholder="请输入你的QQ号" />
<button @click="bindQq">绑定并提交</button>
</div>
</div>

遇到的问题

OneBot 请求失败

错误日志:

1
OneBot请求失败: endpoint=/send_group_msg

排查方向:

  1. Napcat HTTP 服务是否启动
  2. Token 是否配置正确
  3. 后端能否访问 Napcat 地址
  4. 群号是否正确,机器人是否在群里

测试命令:

1
2
3
4
curl -X POST http://127.0.0.1:3000/send_group_msg \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"group_id":"群号","message":"测试"}'

总结

今天完成了:

  • ✅ 游客提交次数统计(按学号)
  • ✅ 成绩统计去重(只取最新)
  • ✅ QQ机器人提醒功能(群提醒+私聊)
  • ✅ 首次提交QQ绑定
  • ✅ 班级学生自动收集

2026-05-10 于桂林