diff --git a/yatori-go/yatori-go-core/aggregation/xuexitong/XueXiTongCourseAction.go b/yatori-go/yatori-go-core/aggregation/xuexitong/XueXiTongCourseAction.go index 553a7d0..6cd8189 100644 --- a/yatori-go/yatori-go-core/aggregation/xuexitong/XueXiTongCourseAction.go +++ b/yatori-go/yatori-go-core/aggregation/xuexitong/XueXiTongCourseAction.go @@ -2,6 +2,9 @@ package xuexitong import ( "encoding/json" + "fmt" + "log" + "sort" "strconv" "strings" "yatori-go-core/api/entity" @@ -56,14 +59,113 @@ func XueXiTCourseDetailForCourseIdAction(cache *xuexitong.XueXiTUserCache, cours return entity.XueXiTCourse{}, nil } +type ChaptersList struct { + ChatID string `json:"chatid"` + Knowledge []KnowledgeItem `json:"knowledge"` +} + +// KnowledgeItem 结构体用于存储 knowledge 中的每个项目 +type KnowledgeItem struct { + JobCount int `json:"jobcount"` // 作业数量 + IsReview int `json:"isreview"` // 是否为复习 + Attachment []interface{} `json:"attachment"` + IndexOrder int `json:"indexorder"` // 节点顺序 + Name string `json:"name"` // 章节名称 + ID int `json:"id"` + Label string `json:"label"` // 节点标签 + Layer int `json:"layer"` // 节点层级 + ParentNodeID int `json:"parentnodeid"` // 父节点 ID + Status string `json:"status"` // 节点状态 +} // PullCourseChapterAction 获取对应课程的章节信息包括节点信息 -func PullCourseChapterAction(cache *xuexitong.XueXiTUserCache, cpi, key int) (string, error) { +func PullCourseChapterAction(cache *xuexitong.XueXiTUserCache, cpi, key int) (ChaptersList, error) { //拉取对应课程的章节信息 chapter, err := cache.PullChapter(cpi, key) if err != nil { log2.Print(log2.INFO, "["+cache.Name+"] "+" 拉取章节失败") - return "", err + return ChaptersList{}, err + } + var chaptersList ChaptersList + var chapterMap map[string]interface{} + err = json.Unmarshal([]byte(chapter), &chapterMap) + if err != nil { + fmt.Println("Error parsing JSON: ", err) + return ChaptersList{}, err + } + chapterMapJson, err := json.Marshal(chapterMap["data"]) + // 解析 JSON 数据为 map 切片 + var chapterData []map[string]interface{} + if err := json.Unmarshal(chapterMapJson, &chapterData); err != nil { + log.Fatalf("Error parsing JSON: %v", err) + } + chatid := chapterData[0]["chatid"].(string) + + // 提取 knowledge + var knowledgeData []map[string]interface{} + course, ok := chapterData[0]["course"].(map[string]interface{}) + if !ok { + fmt.Println("无法提取 course") + return ChaptersList{}, err + } + data, ok := course["data"].([]interface{}) + if !ok { + fmt.Println("无法提取 course data") + return ChaptersList{}, err } - return chapter, nil + if len(data) > 0 { + knowledge, ok := data[0].(map[string]interface{})["knowledge"].(map[string]interface{})["data"].([]interface{}) + if !ok { + fmt.Println("无法提取 knowledge") + return ChaptersList{}, err + } + for _, item := range knowledge { + knowledgeMap := item.(map[string]interface{}) + knowledgeData = append(knowledgeData, knowledgeMap) + } + } else { + fmt.Println("course data 为空") + return ChaptersList{}, err + } + + // 将提取的数据封装到 CourseInfo 结构体中 + var knowledgeItems []KnowledgeItem + for _, item := range knowledgeData { + knowledgeItem := KnowledgeItem{ + JobCount: int(item["jobcount"].(float64)), + IsReview: int(item["isreview"].(float64)), + Attachment: item["attachment"].(map[string]interface{})["data"].([]interface{}), + IndexOrder: int(item["indexorder"].(float64)), + Name: item["name"].(string), + ID: int(item["id"].(float64)), + Label: item["label"].(string), + Layer: int(item["layer"].(float64)), + ParentNodeID: int(item["parentnodeid"].(float64)), + Status: item["status"].(string), + } + knowledgeItems = append(knowledgeItems, knowledgeItem) + } + chaptersList = ChaptersList{ + ChatID: chatid, + Knowledge: knowledgeItems, + } + // 按照任务点节点重排顺序 + sort.Slice(chaptersList.Knowledge, func(i, j int) bool { + iLabelParts := strings.Split(chaptersList.Knowledge[i].Label, ".") + jLabelParts := strings.Split(chaptersList.Knowledge[j].Label, ".") + for k := range iLabelParts { + if k >= len(jLabelParts) { + return false // i has more parts, so it should come after j + } + iv, _ := strconv.Atoi(iLabelParts[k]) + jv, _ := strconv.Atoi(jLabelParts[k]) + if iv != jv { + return iv < jv + } + } + return len(iLabelParts) < len(jLabelParts) + }) + fmt.Printf("获取课程章节成功 (共 %d 个)", + len(chaptersList.Knowledge)) // [%s(Cou.%s/Cla.%s)] + return chaptersList, nil } diff --git a/yatori-go/yatori-go-core/api/xuexitong/XueXiTongChapterApi.go b/yatori-go/yatori-go-core/api/xuexitong/XueXiTongChapterApi.go index 5336264..449fa02 100644 --- a/yatori-go/yatori-go-core/api/xuexitong/XueXiTongChapterApi.go +++ b/yatori-go/yatori-go-core/api/xuexitong/XueXiTongChapterApi.go @@ -46,6 +46,5 @@ func (cache *XueXiTUserCache) PullChapter(cpi int, key int) (string, error) { return "", err } - fmt.Println(string(body)) return string(body), nil } diff --git a/yatori-go/yatori-go-core/examples/yatori_core_test.go b/yatori-go/yatori-go-core/examples/yatori_core_test.go index 35e1f30..ca5c9f5 100644 --- a/yatori-go/yatori-go-core/examples/yatori_core_test.go +++ b/yatori-go/yatori-go-core/examples/yatori_core_test.go @@ -15,89 +15,6 @@ func setup() { common.InitConfig("./") } - -// 账号登录测试 -func TestLogin(t *testing.T) { - utils.YatoriCoreInit() - //测试账号 - setup() - user := global.Config.Users[0] - cache := yinghuaApi.YingHuaUserCache{ - PreUrl: user.URL, - Account: user.Account, - Password: user.Password, - } - error := yinghua.YingHuaLoginAction(&cache) - if error != nil { - - } -} - -// TestLoginXueXiTo 测试学习通登录以及课程数据拉取 -func TestLoginXueXiTo(t *testing.T) { - utils.YatoriCoreInit() - //测试账号 - setup() - user := global.Config.Users[1] - userCache := xuexitongApi.XueXiTUserCache{ - Name: user.Account, - Password: user.Password, - } - err := xuexitong.XueXiTLoginAction(&userCache) - if err != nil { - log.Fatal(err) - } - //拉取课程列表并打印 - xuexitong.XueXiTPullCourseAction(&userCache) -} - -// 测试学习通单课程详情 -func TestCourseDetailXueXiTo(t *testing.T) { - utils.YatoriCoreInit() - //测试账号 - setup() - user := global.Config.Users[1] - userCache := xuexitongApi.XueXiTUserCache{ - Name: user.Account, - Password: user.Password, - } - err := xuexitong.XueXiTLoginAction(&userCache) - if err != nil { - log.Fatal(err) - } - action, err := xuexitong.XueXiTCourseDetailForCourseIdAction(&userCache, "260159398019074") - if err != nil { - log.Fatal(err) - } - fmt.Println(action) - -} - -// TestCourseXueXiToChapter 用于测试学习通对应课程章节信息拉取 -func TestCourseXueXiToChapter(t *testing.T) { - utils.YatoriCoreInit() - //测试账号 - setup() - user := global.Config.Users[1] - userCache := xuexitongApi.XueXiTUserCache{ - Name: user.Account, - Password: user.Password, - } - - err := xuexitong.XueXiTLoginAction(&userCache) - if err != nil { - log.Fatal(err) - } - //拉取对应课程信息 - _, err = xuexitong.XueXiTCourseDetailForCourseIdAction(&userCache, "260159398019074") - //拉取对应课程的章节信息 - chapter, err := xuexitong.PullCourseChapterAction(&userCache, 283918535, 107333284) - if err != nil { - log.Fatal(err) - } - fmt.Println(chapter) -} - // 用于测试Config遵旨的初始化 func TestInitConfig(T *testing.T) { setup() @@ -105,230 +22,6 @@ func TestInitConfig(T *testing.T) { println(users) } - -// 测试获取课程列表 -func TestPullCourseList(t *testing.T) { - utils.YatoriCoreInit() - //测试账号 - setup() - user := global.Config.Users[0] - cache := yinghuaApi.YingHuaUserCache{ - PreUrl: user.URL, - Account: user.Account, - Password: user.Password, - } - - error := yinghua.YingHuaLoginAction(&cache) - if error != nil { - - } - list, _ := yinghua.CourseListAction(&cache) - for _, item := range list { - log2.Print(log2.INFO, "课程:", item.Id, " ", item.Name, " ", strconv.FormatFloat(item.Progress, 'b', 5, 32), " ", item.StartDate.String(), " ", strconv.Itoa(item.VideoCount), " ", strconv.Itoa(item.VideoLearned)) - - } -} - -// 测试拉取对应课程的视屏列表 -func TestPullCourseVideoList(t *testing.T) { - log2.NOWLOGLEVEL = log2.INFO //设置日志登记为DEBUG - //测试账号 - setup() - user := global.Config.Users[0] - cache := yinghuaApi.YingHuaUserCache{ - PreUrl: user.URL, - Account: user.Account, - Password: user.Password, - } - - error := yinghua.YingHuaLoginAction(&cache) - if error != nil { - - } - list, _ := yinghua.CourseListAction(&cache) - for _, courseItem := range list { - log2.Print(log2.INFO, " ", courseItem.Id, " ", courseItem.Name, " ", strconv.FormatFloat(courseItem.Progress, 'b', 5, 32), " ", courseItem.StartDate.String(), " ", strconv.Itoa(courseItem.VideoCount), " ", strconv.Itoa(courseItem.VideoLearned)) - videoList, _ := yinghua.VideosListAction(&cache, courseItem) //拉取视屏列表动作 - for _, videoItem := range videoList { - log2.Print(log2.INFO, " ", "视屏:", videoItem.CourseId, " ", videoItem.Id, " ", videoItem.Name, " ", strconv.Itoa(int(videoItem.VideoDuration))) - } - } - -} - -// 用于登录保活 -func keepAliveLogin(UserCache yinghuaApi.YingHuaUserCache) { - for { - api := yinghuaApi.KeepAliveApi(UserCache) - log2.Print(log2.INFO, " ", "登录保活状态:", api) - time2.Sleep(time2.Second * 60) - } -} - -var wg sync.WaitGroup - -// 刷视频的抽离函数 -func videoListStudy(UserCache yinghuaApi.YingHuaUserCache, course yinghua.YingHuaCourse) { - videoList, _ := yinghua.VideosListAction(&UserCache, course) //拉取对应课程的视屏列表 - - // 提交学时 - for _, video := range videoList { - log2.Print(log2.INFO, " ", video.Name) - time := video.ViewedDuration //设置当前观看时间为最后看视屏的时间 - studyId := "0" - for { - if video.Progress == 100 { - break //如果看完了,也就是进度为100那么直接跳过 - } - sub, _ := yinghuaApi.SubmitStudyTimeApi(UserCache, video.Id, studyId, time) //提交学时 - if gojsonq.New().JSONString(sub).Find("msg") != "提交学时成功!" { - time2.Sleep(5 * time2.Second) - continue - } - - studyId = strconv.Itoa(int(gojsonq.New().JSONString(sub).Find("result.data.studyId").(float64))) - log2.Print(log2.INFO, " ", video.Name, " ", "提交状态:", gojsonq.New().JSONString(sub).Find("msg").(string), " ", "观看时间:", strconv.Itoa(time)+"/"+strconv.Itoa(video.VideoDuration), " ", "观看进度:", fmt.Sprintf("%.2f", float32(time)/float32(video.VideoDuration)*100), "%") - time += 5 - time2.Sleep(5 * time2.Second) - if time > video.VideoDuration { - break //如果看完该视屏则直接下一个 - } - } - } - wg.Done() -} - -// 测试获取指定视屏并且刷课 -func TestBrushOneLesson(t *testing.T) { - utils.YatoriCoreInit() - log2.NOWLOGLEVEL = log2.INFO //设置日志登记为DEBUG - //测试账号 - setup() - user := global.Config.Users[0] - cache := yinghuaApi.YingHuaUserCache{ - PreUrl: user.URL, - Account: user.Account, - Password: user.Password, - } - - error := yinghua.YingHuaLoginAction(&cache) // 登录 - if error != nil { - log.Fatal(error) //登录失败则直接退出 - } - go keepAliveLogin(cache) //携程保活 - list, _ := yinghua.CourseListAction(&cache) //拉取课程列表 - for _, item := range list { - wg.Add(1) - log2.Print(log2.INFO, " ", item.Id, " ", item.Name, " ", strconv.FormatFloat(item.Progress, 'b', 5, 32), " ", item.StartDate.String(), " ", strconv.Itoa(item.VideoCount), " ", strconv.Itoa(item.VideoLearned)) - go videoListStudy(cache, item) //多携程刷课 - } - wg.Wait() -} - -// 测试获取单个课程的详细信息 -func TestCourseDetail(t *testing.T) { - utils.YatoriCoreInit() - //测试账号 - setup() - user := global.Config.Users[0] - cache := yinghuaApi.YingHuaUserCache{ - PreUrl: user.URL, - Account: user.Account, - Password: user.Password, - } - - error := yinghua.YingHuaLoginAction(&cache) // 登录 - if error != nil { - log.Fatal(error) //登录失败则直接退出 - } - fmt.Println(cache.GetToken()) - action, _ := yinghua.CourseDetailAction(&cache, "1012027") - fmt.Println(action) - if error != nil { - log.Fatal(error) - } - -} - -// 测试获取考试的信息 -func TestExamDetail(t *testing.T) { - utils.YatoriCoreInit() - //测试账号 - setup() - user := global.Config.Users[0] - cache := yinghuaApi.YingHuaUserCache{ - PreUrl: user.URL, - Account: user.Account, - Password: user.Password, - } - fmt.Println(cache) - - error := yinghua.YingHuaLoginAction(&cache) // 登录 - if error != nil { - log.Fatal(error) //登录失败则直接退出 - } - list, _ := yinghua.CourseListAction(&cache) //拉取课程列表 - //list[0] - action, error := yinghua.VideosListAction(&cache, list[2]) - if error != nil { - log.Fatal(error) - } - for _, node := range action { - if node.Name != "第一单元 章节测试" { - continue - } - fmt.Println(node) - //api := yinghuaApi.ExamDetailApi(cache, node.Id) - detailAction, _ := yinghua.ExamDetailAction(&cache, node.Id) - //{"_code":9,"status":false,"msg":"考试测试时间还未开始","result":{}} - exam, _ := yinghuaApi.StartExam(cache, node.CourseId, node.Id, detailAction[0].ExamId, 5, nil) - fmt.Println(detailAction) - fmt.Println(exam) - } -} - -// 测试获取作业的信息 -func TestWorkDetail(t *testing.T) { - utils.YatoriCoreInit() - //测试账号 - setup() - user := global.Config.Users[2] - cache := yinghuaApi.YingHuaUserCache{ - PreUrl: user.URL, - Account: user.Account, - Password: user.Password, - } - - error := yinghua.YingHuaLoginAction(&cache) // 登录 - if error != nil { - log.Fatal(error) //登录失败则直接退出 - } - list, _ := yinghua.CourseListAction(&cache) //拉取课程列表 - //list[0] - action, error := yinghua.VideosListAction(&cache, list[0]) - if error != nil { - log.Fatal(error) - } - for _, node := range action { - if node.Name != "作业" { - continue - } - fmt.Println(node) - //获取作业详细信息 - detailAction, _ := yinghua.WorkDetailAction(&cache, node.Id) - ////{"_code":9,"status":false,"msg":"考试测试时间还未开始","result":{}} - //开始写作业 - //yinghua.StartWorkAction(&cache, detailAction[0], global.Config.Setting.AiSetting.AiType, global.Config.Setting.AiSetting.APIKEY) - fmt.Println(detailAction) - //打印最终分数 - s, error := yinghua.WorkedFinallyScoreAction(&cache, detailAction[0]) - if error != nil { - log.Fatal(error) - } - fmt.Println("最高分:", s) - } -} - // ai自动回复测试,使用时请先自己配置好对应TestData里面的API_KEY func TestAiAnswer(t *testing.T) { //测试账号