fix: 修复6个bug并接入CodeStable工作流

Bug修复:
- GetWorkList 使用了错误的 RecordType (RecordStudy→RecordWork)
- AllRecord handler 返回错误的分页信息 (page硬编码1, pageSize用RecordsCount)
- CourseParse creditNode nil panic (加nil检查)
- WebSocket CheckOrigin 安全漏洞 (release模式限制为同源)
- math/rand 可预测 (替换为 crypto/rand)
- GetDiscussList 未实现 (补全实现, 移除重复路由)

其他:
- 接入 CodeStable 工作流体系 (codestable/ 骨架 + AGENTS.md)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-25 19:55:57 +08:00
parent 83ee4bb5ea
commit 2a6732ffe7
23 changed files with 1561 additions and 23 deletions

View File

@@ -193,6 +193,7 @@ func (wk *WK) Login() (bool, error) {
return true, nil
}
// parseLoginResp: 解析登录响应
func (wk *WK) parseLoginResp(body []byte) (LoginResp, error) {
var result LoginResp
if len(body) == 0 {
@@ -273,12 +274,14 @@ func (wk *WK) CourseParse(content string) ([]Course, error) {
var credit float32
creditNode := htmlquery.FindOne(node, `.//div[@class="note"]/div[@class="number"]/span`)
creditStr := strings.TrimSpace(htmlquery.InnerText(creditNode))
creditVal, err := strconv.ParseFloat(creditStr, 32)
if err == nil {
credit = float32(creditVal)
} else {
log.Warn("课程学分转换失败", zap.Error(err))
if creditNode != nil {
creditStr := strings.TrimSpace(htmlquery.InnerText(creditNode))
creditVal, err := strconv.ParseFloat(creditStr, 32)
if err == nil {
credit = float32(creditVal)
} else {
log.Warn("课程学分转换失败", zap.Error(err))
}
}
typeNode := htmlquery.FindOne(node, `.//div[@class="note"]/div[@class="kind"]/span`)
@@ -352,6 +355,11 @@ func (wk *WK) UserInfoParse(content string) (User, error) {
return user, nil
}
// QuestionAnswerParse: 解析题目答案
func (wk *WK) QuestionAnswerParse(content string) (string, error) {
return "", nil
}
// UserGet: 用户信息获取
func (wk *WK) UserInfoGet() (User, error) {
var user User
@@ -467,10 +475,9 @@ func (wk *WK) GetStudyList(courseID, page string) (*AllRecordResp[StudyList], er
return GetRecords[StudyList](wk, RecordStudy, courseID, page)
}
// GetExamList: 获取作业记录
// todo
func (wk *WK) GetWorkList(courseID, page string) (*AllRecordResp[ExamList], error) {
return GetRecords[ExamList](wk, RecordStudy, courseID, page)
// GetWorkList: 获取作业记录
func (wk *WK) GetWorkList(courseID, page string) (*AllRecordResp[WorkList], error) {
return GetRecords[WorkList](wk, RecordWork, courseID, page)
}
// GetExamList: 获取考试记录
@@ -478,10 +485,9 @@ func (wk *WK) GetExamList(courseID, page string) (*AllRecordResp[ExamList], erro
return GetRecords[ExamList](wk, RecordExam, courseID, page)
}
// GetExamList: 获取讨论记录
// todo
func (wk *WK) GetDiscussList(courseID, page string) (*AllRecordResp[ExamList], error) {
return GetRecords[ExamList](wk, RecordStudy, courseID, page)
// GetDiscussList: 获取讨论记录
func (wk *WK) GetDiscussList(courseID, page string) (*AllRecordResp[StudyList], error) {
return GetRecords[StudyList](wk, RecordDiscuss, courseID, page)
}
func isLoginTimeoutBody(body []byte) bool {

View File

@@ -77,6 +77,44 @@ type StudyList struct {
URL string `json:"url"`
}
// 作业记录列表
type WorkList struct {
ID string `json:"id"`
UserID any `json:"userId"`
Title string `json:"title"`
TopicNumber string `json:"topicNumber"`
Score string `json:"score"`
Type string `json:"type"`
Remarks string `json:"remarks"`
AddTime string `json:"addTime"`
Sequence string `json:"sequence"`
NodeID string `json:"nodeId"`
CourseID string `json:"courseId"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
PaperID string `json:"paperId"`
CreateUserID string `json:"createUserId"`
IsPrivate string `json:"isPrivate"`
ClassList string `json:"classList"`
TeacherType string `json:"teacherType"`
Allow string `json:"allow"`
Frequency string `json:"frequency"`
ScoringRules string `json:"scoringRules"`
HasCollect string `json:"hasCollect"`
Lock any `json:"lock"`
SchoolID string `json:"schoolId"`
Parsing string `json:"parsing"`
AddDate string `json:"addDate"`
Name string `json:"name"`
ChapterID string `json:"chapterId"`
State string `json:"state"`
SubmitTime string `json:"submitTime"`
FinalScore string `json:"finalScore"`
TypeName string `json:"typeName"`
FinishTime string `json:"finishTime"`
URL string `json:"url"`
}
// 考试记录列表
type ExamList struct {
ID string `json:"id"`

View File

@@ -49,6 +49,9 @@ type Course struct {
Type string `json:"type"`
}
type QAList struct {
}
func GetRecords[T any](wk *WK, rType RecordType, courseID, page string) (*AllRecordResp[T], error) {
log.Debug("获取记录信息", zap.String("host", wk.Host), zap.String("type", string(rType)))
resp, err := wk.newRequest().