feat(api): implement exam and grade synchronization modules
All checks were successful
Build Backend / build (push) Successful in 2m18s
Build Backend / build-docker (push) Successful in 1m20s

Add new functionality for managing and synchronizing academic grades and exam schedules. This includes new models, repositories, services, and HTTP handlers, along with updated gRPC definitions and dependency injection configuration.

Additionally, improve the reliability of unread message count tracking by implementing an atomic Lua script for Redis operations in the conversation cache.
This commit is contained in:
2026-05-12 01:28:18 +08:00
parent 628a6acbe9
commit 2f2bbc646e
20 changed files with 1052 additions and 212 deletions

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.1
// protoc v7.34.1
// source: proto/runner/runner.proto
package runner
@@ -1209,19 +1209,105 @@ func (x *ScheduleTime) GetWeeks() []int32 {
return nil
}
// GpaSummary 学分绩汇总信息
type GpaSummary struct {
state protoimpl.MessageState `protogen:"open.v1"`
AverageGpa string `protobuf:"bytes,1,opt,name=average_gpa,json=averageGpa,proto3" json:"average_gpa,omitempty"` // 平均学分绩
Rank string `protobuf:"bytes,2,opt,name=rank,proto3" json:"rank,omitempty"` // 专业排名
ExamTotalGpa string `protobuf:"bytes,3,opt,name=exam_total_gpa,json=examTotalGpa,proto3" json:"exam_total_gpa,omitempty"` // 考试课学分绩
ExamTotalCredits string `protobuf:"bytes,4,opt,name=exam_total_credits,json=examTotalCredits,proto3" json:"exam_total_credits,omitempty"` // 考试课学分数
FailCredits string `protobuf:"bytes,5,opt,name=fail_credits,json=failCredits,proto3" json:"fail_credits,omitempty"` // 考查课不及格学分
Semesters string `protobuf:"bytes,6,opt,name=semesters,proto3" json:"semesters,omitempty"` // 计算学期数
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GpaSummary) Reset() {
*x = GpaSummary{}
mi := &file_proto_runner_runner_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GpaSummary) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GpaSummary) ProtoMessage() {}
func (x *GpaSummary) ProtoReflect() protoreflect.Message {
mi := &file_proto_runner_runner_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GpaSummary.ProtoReflect.Descriptor instead.
func (*GpaSummary) Descriptor() ([]byte, []int) {
return file_proto_runner_runner_proto_rawDescGZIP(), []int{15}
}
func (x *GpaSummary) GetAverageGpa() string {
if x != nil {
return x.AverageGpa
}
return ""
}
func (x *GpaSummary) GetRank() string {
if x != nil {
return x.Rank
}
return ""
}
func (x *GpaSummary) GetExamTotalGpa() string {
if x != nil {
return x.ExamTotalGpa
}
return ""
}
func (x *GpaSummary) GetExamTotalCredits() string {
if x != nil {
return x.ExamTotalCredits
}
return ""
}
func (x *GpaSummary) GetFailCredits() string {
if x != nil {
return x.FailCredits
}
return ""
}
func (x *GpaSummary) GetSemesters() string {
if x != nil {
return x.Semesters
}
return ""
}
// GradesResultData 成绩结果数据
type GradesResultData struct {
state protoimpl.MessageState `protogen:"open.v1"`
Semester string `protobuf:"bytes,1,opt,name=semester,proto3" json:"semester,omitempty"` // 学期
StudentId string `protobuf:"bytes,2,opt,name=student_id,json=studentId,proto3" json:"student_id,omitempty"` // 学号
Grades []*Grade `protobuf:"bytes,3,rep,name=grades,proto3" json:"grades,omitempty"` // 成绩列表
Semester string `protobuf:"bytes,1,opt,name=semester,proto3" json:"semester,omitempty"` // 学期
StudentId string `protobuf:"bytes,2,opt,name=student_id,json=studentId,proto3" json:"student_id,omitempty"` // 学号
Grades []*Grade `protobuf:"bytes,3,rep,name=grades,proto3" json:"grades,omitempty"` // 成绩列表
GpaSummary *GpaSummary `protobuf:"bytes,4,opt,name=gpa_summary,json=gpaSummary,proto3" json:"gpa_summary,omitempty"` // 学分绩汇总 (可选)
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GradesResultData) Reset() {
*x = GradesResultData{}
mi := &file_proto_runner_runner_proto_msgTypes[15]
mi := &file_proto_runner_runner_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1233,7 +1319,7 @@ func (x *GradesResultData) String() string {
func (*GradesResultData) ProtoMessage() {}
func (x *GradesResultData) ProtoReflect() protoreflect.Message {
mi := &file_proto_runner_runner_proto_msgTypes[15]
mi := &file_proto_runner_runner_proto_msgTypes[16]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1246,7 +1332,7 @@ func (x *GradesResultData) ProtoReflect() protoreflect.Message {
// Deprecated: Use GradesResultData.ProtoReflect.Descriptor instead.
func (*GradesResultData) Descriptor() ([]byte, []int) {
return file_proto_runner_runner_proto_rawDescGZIP(), []int{15}
return file_proto_runner_runner_proto_rawDescGZIP(), []int{16}
}
func (x *GradesResultData) GetSemester() string {
@@ -1270,21 +1356,32 @@ func (x *GradesResultData) GetGrades() []*Grade {
return nil
}
func (x *GradesResultData) GetGpaSummary() *GpaSummary {
if x != nil {
return x.GpaSummary
}
return nil
}
// Grade 成绩信息
type Grade struct {
state protoimpl.MessageState `protogen:"open.v1"`
CourseCode string `protobuf:"bytes,1,opt,name=course_code,json=courseCode,proto3" json:"course_code,omitempty"` // 课程代码
CourseName string `protobuf:"bytes,2,opt,name=course_name,json=courseName,proto3" json:"course_name,omitempty"` // 课程名称
Credit float32 `protobuf:"fixed32,3,opt,name=credit,proto3" json:"credit,omitempty"` // 学分
Grade string `protobuf:"bytes,4,opt,name=grade,proto3" json:"grade,omitempty"` // 成绩 (可能是等级或分数)
GradePoint float32 `protobuf:"fixed32,5,opt,name=grade_point,json=gradePoint,proto3" json:"grade_point,omitempty"` // 绩点
Term string `protobuf:"bytes,1,opt,name=term,proto3" json:"term,omitempty"` // 学期
CourseCode string `protobuf:"bytes,2,opt,name=course_code,json=courseCode,proto3" json:"course_code,omitempty"` // 课程代码
CourseName string `protobuf:"bytes,3,opt,name=course_name,json=courseName,proto3" json:"course_name,omitempty"` // 课程名称
Nature string `protobuf:"bytes,4,opt,name=nature,proto3" json:"nature,omitempty"` // 课程性质
Category string `protobuf:"bytes,5,opt,name=category,proto3" json:"category,omitempty"` // 课程类别
Credit string `protobuf:"bytes,6,opt,name=credit,proto3" json:"credit,omitempty"` // 学分
Score string `protobuf:"bytes,7,opt,name=score,proto3" json:"score,omitempty"` // 成绩 (可能是等级或分数)
GradePoint string `protobuf:"bytes,8,opt,name=grade_point,json=gradePoint,proto3" json:"grade_point,omitempty"` // 绩点
IsExam string `protobuf:"bytes,9,opt,name=is_exam,json=isExam,proto3" json:"is_exam,omitempty"` // 是否考试课
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Grade) Reset() {
*x = Grade{}
mi := &file_proto_runner_runner_proto_msgTypes[16]
mi := &file_proto_runner_runner_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1296,7 +1393,7 @@ func (x *Grade) String() string {
func (*Grade) ProtoMessage() {}
func (x *Grade) ProtoReflect() protoreflect.Message {
mi := &file_proto_runner_runner_proto_msgTypes[16]
mi := &file_proto_runner_runner_proto_msgTypes[17]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1309,7 +1406,14 @@ func (x *Grade) ProtoReflect() protoreflect.Message {
// Deprecated: Use Grade.ProtoReflect.Descriptor instead.
func (*Grade) Descriptor() ([]byte, []int) {
return file_proto_runner_runner_proto_rawDescGZIP(), []int{16}
return file_proto_runner_runner_proto_rawDescGZIP(), []int{17}
}
func (x *Grade) GetTerm() string {
if x != nil {
return x.Term
}
return ""
}
func (x *Grade) GetCourseCode() string {
@@ -1326,25 +1430,46 @@ func (x *Grade) GetCourseName() string {
return ""
}
func (x *Grade) GetCredit() float32 {
func (x *Grade) GetNature() string {
if x != nil {
return x.Credit
}
return 0
}
func (x *Grade) GetGrade() string {
if x != nil {
return x.Grade
return x.Nature
}
return ""
}
func (x *Grade) GetGradePoint() float32 {
func (x *Grade) GetCategory() string {
if x != nil {
return x.Category
}
return ""
}
func (x *Grade) GetCredit() string {
if x != nil {
return x.Credit
}
return ""
}
func (x *Grade) GetScore() string {
if x != nil {
return x.Score
}
return ""
}
func (x *Grade) GetGradePoint() string {
if x != nil {
return x.GradePoint
}
return 0
return ""
}
func (x *Grade) GetIsExam() string {
if x != nil {
return x.IsExam
}
return ""
}
// ExamsResultData 考试安排结果数据
@@ -1359,7 +1484,7 @@ type ExamsResultData struct {
func (x *ExamsResultData) Reset() {
*x = ExamsResultData{}
mi := &file_proto_runner_runner_proto_msgTypes[17]
mi := &file_proto_runner_runner_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1371,7 +1496,7 @@ func (x *ExamsResultData) String() string {
func (*ExamsResultData) ProtoMessage() {}
func (x *ExamsResultData) ProtoReflect() protoreflect.Message {
mi := &file_proto_runner_runner_proto_msgTypes[17]
mi := &file_proto_runner_runner_proto_msgTypes[18]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1384,7 +1509,7 @@ func (x *ExamsResultData) ProtoReflect() protoreflect.Message {
// Deprecated: Use ExamsResultData.ProtoReflect.Descriptor instead.
func (*ExamsResultData) Descriptor() ([]byte, []int) {
return file_proto_runner_runner_proto_rawDescGZIP(), []int{17}
return file_proto_runner_runner_proto_rawDescGZIP(), []int{18}
}
func (x *ExamsResultData) GetSemester() string {
@@ -1411,20 +1536,19 @@ func (x *ExamsResultData) GetExams() []*Exam {
// Exam 考试信息
type Exam struct {
state protoimpl.MessageState `protogen:"open.v1"`
CourseCode string `protobuf:"bytes,1,opt,name=course_code,json=courseCode,proto3" json:"course_code,omitempty"` // 课程代码
CourseName string `protobuf:"bytes,2,opt,name=course_name,json=courseName,proto3" json:"course_name,omitempty"` // 课程名称
ExamType string `protobuf:"bytes,3,opt,name=exam_type,json=examType,proto3" json:"exam_type,omitempty"` // 考试类型 (期末/期中/补考)
Date string `protobuf:"bytes,4,opt,name=date,proto3" json:"date,omitempty"` // 考试日期 (YYYY-MM-DD)
Time string `protobuf:"bytes,5,opt,name=time,proto3" json:"time,omitempty"` // 考试时间 (HH:MM-HH:MM)
Room string `protobuf:"bytes,6,opt,name=room,proto3" json:"room,omitempty"` // 考场
Seat string `protobuf:"bytes,7,opt,name=seat,proto3" json:"seat,omitempty"` // 座位号
CourseName string `protobuf:"bytes,1,opt,name=course_name,json=courseName,proto3" json:"course_name,omitempty"` // 课程名称
ExamType string `protobuf:"bytes,2,opt,name=exam_type,json=examType,proto3" json:"exam_type,omitempty"` // 考试类型 (期末/期中/补考)
Date string `protobuf:"bytes,3,opt,name=date,proto3" json:"date,omitempty"` // 考试日期
Time string `protobuf:"bytes,4,opt,name=time,proto3" json:"time,omitempty"` // 考试时间
Week string `protobuf:"bytes,5,opt,name=week,proto3" json:"week,omitempty"` // 考试周次
Weekday string `protobuf:"bytes,6,opt,name=weekday,proto3" json:"weekday,omitempty"` // 星期几
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Exam) Reset() {
*x = Exam{}
mi := &file_proto_runner_runner_proto_msgTypes[18]
mi := &file_proto_runner_runner_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1436,7 +1560,7 @@ func (x *Exam) String() string {
func (*Exam) ProtoMessage() {}
func (x *Exam) ProtoReflect() protoreflect.Message {
mi := &file_proto_runner_runner_proto_msgTypes[18]
mi := &file_proto_runner_runner_proto_msgTypes[19]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1449,14 +1573,7 @@ func (x *Exam) ProtoReflect() protoreflect.Message {
// Deprecated: Use Exam.ProtoReflect.Descriptor instead.
func (*Exam) Descriptor() ([]byte, []int) {
return file_proto_runner_runner_proto_rawDescGZIP(), []int{18}
}
func (x *Exam) GetCourseCode() string {
if x != nil {
return x.CourseCode
}
return ""
return file_proto_runner_runner_proto_rawDescGZIP(), []int{19}
}
func (x *Exam) GetCourseName() string {
@@ -1487,16 +1604,16 @@ func (x *Exam) GetTime() string {
return ""
}
func (x *Exam) GetRoom() string {
func (x *Exam) GetWeek() string {
if x != nil {
return x.Room
return x.Week
}
return ""
}
func (x *Exam) GetSeat() string {
func (x *Exam) GetWeekday() string {
if x != nil {
return x.Seat
return x.Weekday
}
return ""
}
@@ -1517,7 +1634,7 @@ type UserInfoResultData struct {
func (x *UserInfoResultData) Reset() {
*x = UserInfoResultData{}
mi := &file_proto_runner_runner_proto_msgTypes[19]
mi := &file_proto_runner_runner_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1529,7 +1646,7 @@ func (x *UserInfoResultData) String() string {
func (*UserInfoResultData) ProtoMessage() {}
func (x *UserInfoResultData) ProtoReflect() protoreflect.Message {
mi := &file_proto_runner_runner_proto_msgTypes[19]
mi := &file_proto_runner_runner_proto_msgTypes[20]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1542,7 +1659,7 @@ func (x *UserInfoResultData) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserInfoResultData.ProtoReflect.Descriptor instead.
func (*UserInfoResultData) Descriptor() ([]byte, []int) {
return file_proto_runner_runner_proto_rawDescGZIP(), []int{19}
return file_proto_runner_runner_proto_rawDescGZIP(), []int{20}
}
func (x *UserInfoResultData) GetStudentId() string {
@@ -1680,36 +1797,49 @@ const file_proto_runner_runner_proto_rawDesc = "" +
"\fScheduleTime\x12\x10\n" +
"\x03day\x18\x01 \x01(\tR\x03day\x12\x1a\n" +
"\bsections\x18\x02 \x03(\x05R\bsections\x12\x14\n" +
"\x05weeks\x18\x03 \x03(\x05R\x05weeks\"t\n" +
"\x05weeks\x18\x03 \x03(\x05R\x05weeks\"\xd6\x01\n" +
"\n" +
"GpaSummary\x12\x1f\n" +
"\vaverage_gpa\x18\x01 \x01(\tR\n" +
"averageGpa\x12\x12\n" +
"\x04rank\x18\x02 \x01(\tR\x04rank\x12$\n" +
"\x0eexam_total_gpa\x18\x03 \x01(\tR\fexamTotalGpa\x12,\n" +
"\x12exam_total_credits\x18\x04 \x01(\tR\x10examTotalCredits\x12!\n" +
"\ffail_credits\x18\x05 \x01(\tR\vfailCredits\x12\x1c\n" +
"\tsemesters\x18\x06 \x01(\tR\tsemesters\"\xa9\x01\n" +
"\x10GradesResultData\x12\x1a\n" +
"\bsemester\x18\x01 \x01(\tR\bsemester\x12\x1d\n" +
"\n" +
"student_id\x18\x02 \x01(\tR\tstudentId\x12%\n" +
"\x06grades\x18\x03 \x03(\v2\r.runner.GradeR\x06grades\"\x98\x01\n" +
"\x05Grade\x12\x1f\n" +
"\vcourse_code\x18\x01 \x01(\tR\n" +
"\x06grades\x18\x03 \x03(\v2\r.runner.GradeR\x06grades\x123\n" +
"\vgpa_summary\x18\x04 \x01(\v2\x12.runner.GpaSummaryR\n" +
"gpaSummary\"\xf9\x01\n" +
"\x05Grade\x12\x12\n" +
"\x04term\x18\x01 \x01(\tR\x04term\x12\x1f\n" +
"\vcourse_code\x18\x02 \x01(\tR\n" +
"courseCode\x12\x1f\n" +
"\vcourse_name\x18\x02 \x01(\tR\n" +
"\vcourse_name\x18\x03 \x01(\tR\n" +
"courseName\x12\x16\n" +
"\x06credit\x18\x03 \x01(\x02R\x06credit\x12\x14\n" +
"\x05grade\x18\x04 \x01(\tR\x05grade\x12\x1f\n" +
"\vgrade_point\x18\x05 \x01(\x02R\n" +
"gradePoint\"p\n" +
"\x06nature\x18\x04 \x01(\tR\x06nature\x12\x1a\n" +
"\bcategory\x18\x05 \x01(\tR\bcategory\x12\x16\n" +
"\x06credit\x18\x06 \x01(\tR\x06credit\x12\x14\n" +
"\x05score\x18\a \x01(\tR\x05score\x12\x1f\n" +
"\vgrade_point\x18\b \x01(\tR\n" +
"gradePoint\x12\x17\n" +
"\ais_exam\x18\t \x01(\tR\x06isExam\"p\n" +
"\x0fExamsResultData\x12\x1a\n" +
"\bsemester\x18\x01 \x01(\tR\bsemester\x12\x1d\n" +
"\n" +
"student_id\x18\x02 \x01(\tR\tstudentId\x12\"\n" +
"\x05exams\x18\x03 \x03(\v2\f.runner.ExamR\x05exams\"\xb5\x01\n" +
"\x05exams\x18\x03 \x03(\v2\f.runner.ExamR\x05exams\"\x9a\x01\n" +
"\x04Exam\x12\x1f\n" +
"\vcourse_code\x18\x01 \x01(\tR\n" +
"courseCode\x12\x1f\n" +
"\vcourse_name\x18\x02 \x01(\tR\n" +
"\vcourse_name\x18\x01 \x01(\tR\n" +
"courseName\x12\x1b\n" +
"\texam_type\x18\x03 \x01(\tR\bexamType\x12\x12\n" +
"\x04date\x18\x04 \x01(\tR\x04date\x12\x12\n" +
"\x04time\x18\x05 \x01(\tR\x04time\x12\x12\n" +
"\x04room\x18\x06 \x01(\tR\x04room\x12\x12\n" +
"\x04seat\x18\a \x01(\tR\x04seat\"\xc4\x01\n" +
"\texam_type\x18\x02 \x01(\tR\bexamType\x12\x12\n" +
"\x04date\x18\x03 \x01(\tR\x04date\x12\x12\n" +
"\x04time\x18\x04 \x01(\tR\x04time\x12\x12\n" +
"\x04week\x18\x05 \x01(\tR\x04week\x12\x18\n" +
"\aweekday\x18\x06 \x01(\tR\aweekday\"\xc4\x01\n" +
"\x12UserInfoResultData\x12\x1d\n" +
"\n" +
"student_id\x18\x01 \x01(\tR\tstudentId\x12\x12\n" +
@@ -1735,7 +1865,7 @@ const file_proto_runner_runner_proto_rawDesc = "" +
"\x13TASK_STATUS_TIMEOUT\x10\x03\x12\x19\n" +
"\x15TASK_STATUS_CANCELLED\x10\x042H\n" +
"\tRunnerHub\x12;\n" +
"\aConnect\x12\x15.runner.StreamMessage\x1a\x15.runner.StreamMessage(\x010\x01B Z\x1ecarrot_bbs/proto/runner;runnerb\x06proto3"
"\aConnect\x12\x15.runner.StreamMessage\x1a\x15.runner.StreamMessage(\x010\x01B(Z&schedule_converter/proto/runner;runnerb\x06proto3"
var (
file_proto_runner_runner_proto_rawDescOnce sync.Once
@@ -1750,7 +1880,7 @@ func file_proto_runner_runner_proto_rawDescGZIP() []byte {
}
var file_proto_runner_runner_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_proto_runner_runner_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
var file_proto_runner_runner_proto_msgTypes = make([]protoimpl.MessageInfo, 22)
var file_proto_runner_runner_proto_goTypes = []any{
(TaskType)(0), // 0: runner.TaskType
(TaskStatus)(0), // 1: runner.TaskStatus
@@ -1769,12 +1899,13 @@ var file_proto_runner_runner_proto_goTypes = []any{
(*ScheduleResultData)(nil), // 14: runner.ScheduleResultData
(*Course)(nil), // 15: runner.Course
(*ScheduleTime)(nil), // 16: runner.ScheduleTime
(*GradesResultData)(nil), // 17: runner.GradesResultData
(*Grade)(nil), // 18: runner.Grade
(*ExamsResultData)(nil), // 19: runner.ExamsResultData
(*Exam)(nil), // 20: runner.Exam
(*UserInfoResultData)(nil), // 21: runner.UserInfoResultData
nil, // 22: runner.RegisterRequest.CapabilitiesEntry
(*GpaSummary)(nil), // 17: runner.GpaSummary
(*GradesResultData)(nil), // 18: runner.GradesResultData
(*Grade)(nil), // 19: runner.Grade
(*ExamsResultData)(nil), // 20: runner.ExamsResultData
(*Exam)(nil), // 21: runner.Exam
(*UserInfoResultData)(nil), // 22: runner.UserInfoResultData
nil, // 23: runner.RegisterRequest.CapabilitiesEntry
}
var file_proto_runner_runner_proto_depIdxs = []int32{
3, // 0: runner.StreamMessage.register:type_name -> runner.RegisterRequest
@@ -1784,20 +1915,21 @@ var file_proto_runner_runner_proto_depIdxs = []int32{
7, // 4: runner.StreamMessage.task:type_name -> runner.Task
9, // 5: runner.StreamMessage.result:type_name -> runner.TaskResult
8, // 6: runner.StreamMessage.task_cancel:type_name -> runner.TaskCancel
22, // 7: runner.RegisterRequest.capabilities:type_name -> runner.RegisterRequest.CapabilitiesEntry
23, // 7: runner.RegisterRequest.capabilities:type_name -> runner.RegisterRequest.CapabilitiesEntry
0, // 8: runner.Task.type:type_name -> runner.TaskType
1, // 9: runner.TaskResult.status:type_name -> runner.TaskStatus
15, // 10: runner.ScheduleResultData.courses:type_name -> runner.Course
16, // 11: runner.Course.schedule:type_name -> runner.ScheduleTime
18, // 12: runner.GradesResultData.grades:type_name -> runner.Grade
20, // 13: runner.ExamsResultData.exams:type_name -> runner.Exam
2, // 14: runner.RunnerHub.Connect:input_type -> runner.StreamMessage
2, // 15: runner.RunnerHub.Connect:output_type -> runner.StreamMessage
15, // [15:16] is the sub-list for method output_type
14, // [14:15] is the sub-list for method input_type
14, // [14:14] is the sub-list for extension type_name
14, // [14:14] is the sub-list for extension extendee
0, // [0:14] is the sub-list for field type_name
19, // 12: runner.GradesResultData.grades:type_name -> runner.Grade
17, // 13: runner.GradesResultData.gpa_summary:type_name -> runner.GpaSummary
21, // 14: runner.ExamsResultData.exams:type_name -> runner.Exam
2, // 15: runner.RunnerHub.Connect:input_type -> runner.StreamMessage
2, // 16: runner.RunnerHub.Connect:output_type -> runner.StreamMessage
16, // [16:17] is the sub-list for method output_type
15, // [15:16] is the sub-list for method input_type
15, // [15:15] is the sub-list for extension type_name
15, // [15:15] is the sub-list for extension extendee
0, // [0:15] is the sub-list for field type_name
}
func init() { file_proto_runner_runner_proto_init() }
@@ -1820,7 +1952,7 @@ func file_proto_runner_runner_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_runner_runner_proto_rawDesc), len(file_proto_runner_runner_proto_rawDesc)),
NumEnums: 2,
NumMessages: 21,
NumMessages: 22,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -2,7 +2,7 @@ syntax = "proto3";
package runner;
option go_package = "carrot_bbs/proto/runner;runner";
option go_package = "schedule_converter/proto/runner;runner";
// ============================================================
// 服务定义
@@ -178,20 +178,35 @@ message ScheduleTime {
repeated int32 weeks = 3; // 周次,如 [1, 2, 3, 4, 5]
}
// GpaSummary 学分绩汇总信息
message GpaSummary {
string average_gpa = 1; // 平均学分绩
string rank = 2; // 专业排名
string exam_total_gpa = 3; // 考试课学分绩
string exam_total_credits = 4; // 考试课学分数
string fail_credits = 5; // 考查课不及格学分
string semesters = 6; // 计算学期数
}
// GradesResultData 成绩结果数据
message GradesResultData {
string semester = 1; // 学期
string student_id = 2; // 学号
repeated Grade grades = 3; // 成绩列表
GpaSummary gpa_summary = 4; // 学分绩汇总 (可选)
}
// Grade 成绩信息
message Grade {
string course_code = 1; // 课程代码
string course_name = 2; // 课程名称
float credit = 3; // 学分
string grade = 4; // 成绩 (可能是等级或分数)
float grade_point = 5; // 绩点
string term = 1; // 学期
string course_code = 2; // 课程代码
string course_name = 3; // 课程名称
string nature = 4; // 课程性质
string category = 5; // 课程类别
string credit = 6; // 学分
string score = 7; // 成绩 (可能是等级或分数)
string grade_point = 8; // 绩点
string is_exam = 9; // 是否考试课
}
// ExamsResultData 考试安排结果数据
@@ -203,13 +218,12 @@ message ExamsResultData {
// Exam 考试信息
message Exam {
string course_code = 1; // 课程代码
string course_name = 2; // 课程名称
string exam_type = 3; // 考试类型 (期末/期中/补考)
string date = 4; // 考试日期 (YYYY-MM-DD)
string time = 5; // 考试时间 (HH:MM-HH:MM)
string room = 6; // 考场
string seat = 7; // 座位号
string course_name = 1; // 课程名称
string exam_type = 2; // 考试类型 (期末/期中/补考)
string date = 3; // 考试日期
string time = 4; // 考试时间
string week = 5; // 考试周次
string weekday = 6; // 星期几
}
// UserInfoResultData 用户信息结果数据

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v6.33.1
// - protoc-gen-go-grpc v1.6.1
// - protoc v7.34.1
// source: proto/runner/runner.proto
package runner
@@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
RunnerHub_Connect_FullMethodName = "/runner.RunnerHub/Connect"
@@ -25,10 +25,13 @@ const (
// RunnerHubClient is the client API for RunnerHub service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// RunnerHub 服务 - 由 Backend 服务器实现
// Runner 作为客户端主动连接,建立双向流通信
type RunnerHubClient interface {
// Connect 建立双向流连接
// Runner 通过此连接注册、接收任务、返回结果
Connect(ctx context.Context, opts ...grpc.CallOption) (RunnerHub_ConnectClient, error)
Connect(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[StreamMessage, StreamMessage], error)
}
type runnerHubClient struct {
@@ -39,55 +42,44 @@ func NewRunnerHubClient(cc grpc.ClientConnInterface) RunnerHubClient {
return &runnerHubClient{cc}
}
func (c *runnerHubClient) Connect(ctx context.Context, opts ...grpc.CallOption) (RunnerHub_ConnectClient, error) {
stream, err := c.cc.NewStream(ctx, &RunnerHub_ServiceDesc.Streams[0], RunnerHub_Connect_FullMethodName, opts...)
func (c *runnerHubClient) Connect(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[StreamMessage, StreamMessage], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &RunnerHub_ServiceDesc.Streams[0], RunnerHub_Connect_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &runnerHubConnectClient{stream}
x := &grpc.GenericClientStream[StreamMessage, StreamMessage]{ClientStream: stream}
return x, nil
}
type RunnerHub_ConnectClient interface {
Send(*StreamMessage) error
Recv() (*StreamMessage, error)
grpc.ClientStream
}
type runnerHubConnectClient struct {
grpc.ClientStream
}
func (x *runnerHubConnectClient) Send(m *StreamMessage) error {
return x.ClientStream.SendMsg(m)
}
func (x *runnerHubConnectClient) Recv() (*StreamMessage, error) {
m := new(StreamMessage)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type RunnerHub_ConnectClient = grpc.BidiStreamingClient[StreamMessage, StreamMessage]
// RunnerHubServer is the server API for RunnerHub service.
// All implementations must embed UnimplementedRunnerHubServer
// for forward compatibility
// for forward compatibility.
//
// RunnerHub 服务 - 由 Backend 服务器实现
// Runner 作为客户端主动连接,建立双向流通信
type RunnerHubServer interface {
// Connect 建立双向流连接
// Runner 通过此连接注册、接收任务、返回结果
Connect(RunnerHub_ConnectServer) error
Connect(grpc.BidiStreamingServer[StreamMessage, StreamMessage]) error
mustEmbedUnimplementedRunnerHubServer()
}
// UnimplementedRunnerHubServer must be embedded to have forward compatible implementations.
type UnimplementedRunnerHubServer struct {
}
// UnimplementedRunnerHubServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedRunnerHubServer struct{}
func (UnimplementedRunnerHubServer) Connect(RunnerHub_ConnectServer) error {
return status.Errorf(codes.Unimplemented, "method Connect not implemented")
func (UnimplementedRunnerHubServer) Connect(grpc.BidiStreamingServer[StreamMessage, StreamMessage]) error {
return status.Error(codes.Unimplemented, "method Connect not implemented")
}
func (UnimplementedRunnerHubServer) mustEmbedUnimplementedRunnerHubServer() {}
func (UnimplementedRunnerHubServer) testEmbeddedByValue() {}
// UnsafeRunnerHubServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to RunnerHubServer will
@@ -97,34 +89,22 @@ type UnsafeRunnerHubServer interface {
}
func RegisterRunnerHubServer(s grpc.ServiceRegistrar, srv RunnerHubServer) {
// If the following call panics, it indicates UnimplementedRunnerHubServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&RunnerHub_ServiceDesc, srv)
}
func _RunnerHub_Connect_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(RunnerHubServer).Connect(&runnerHubConnectServer{stream})
return srv.(RunnerHubServer).Connect(&grpc.GenericServerStream[StreamMessage, StreamMessage]{ServerStream: stream})
}
type RunnerHub_ConnectServer interface {
Send(*StreamMessage) error
Recv() (*StreamMessage, error)
grpc.ServerStream
}
type runnerHubConnectServer struct {
grpc.ServerStream
}
func (x *runnerHubConnectServer) Send(m *StreamMessage) error {
return x.ServerStream.SendMsg(m)
}
func (x *runnerHubConnectServer) Recv() (*StreamMessage, error) {
m := new(StreamMessage)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type RunnerHub_ConnectServer = grpc.BidiStreamingServer[StreamMessage, StreamMessage]
// RunnerHub_ServiceDesc is the grpc.ServiceDesc for RunnerHub service.
// It's only intended for direct use with grpc.RegisterService,