- 提取 getWKFromContext 辅助函数,消除 handler 中 5 处重复代码 - 提取 retryCode 函数,消除 Login/performStudy 中验证码重试重复 - 提取 removeSession 内部方法,消除 Del/ClearAll/ClearExpired 中 3 处重复 - 提取 WK.UserKey() 方法,消除 4 处 userKey 手动拼接 - SessionManager.Get() 改用 RLock 优化读性能 - GetRecords 递归分页改为迭代,避免栈溢出 - prepareRequestClient 添加配置缓存,仅在 debug 设置变化时重建 - 修正 schedule.go 时区为 Asia/Shanghai + cron "0 6 * * *" - 修正 typo "以达到" → "已达到" - 删除未使用的 QAList struct - 修复 bufferHub.append 切片内存泄漏 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
245 lines
5.2 KiB
Go
245 lines
5.2 KiB
Go
package handler
|
||
|
||
import (
|
||
"ckwk/internal/ckwk"
|
||
"ckwk/internal/dto"
|
||
"ckwk/pkg/log"
|
||
"errors"
|
||
"fmt"
|
||
"net/http"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
type WKHandler struct {
|
||
Session *ckwk.SessionManager
|
||
}
|
||
|
||
func NewWKHandler() *WKHandler {
|
||
return &WKHandler{
|
||
Session: ckwk.NewSessionManager(),
|
||
}
|
||
}
|
||
|
||
// getWKFromContext 从 gin.Context 中提取 wk_instance,不存在时自动返回错误响应
|
||
func getWKFromContext(ctx *gin.Context) (*ckwk.WK, bool) {
|
||
val, ok := ctx.Get("wk_instance")
|
||
if !ok {
|
||
ctx.JSON(200, dto.Error(-1, "登录已过期"))
|
||
return nil, false
|
||
}
|
||
return val.(*ckwk.WK), true
|
||
}
|
||
|
||
func (h *WKHandler) Login(ctx *gin.Context) {
|
||
var req dto.LoginReq
|
||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||
ctx.JSON(200, dto.Error(-1, "请求参数错误"))
|
||
return
|
||
}
|
||
log.Debug("请求数据", zap.Any("req", req))
|
||
var cookies []*http.Cookie
|
||
if req.Token != "" {
|
||
cookies = []*http.Cookie{
|
||
{
|
||
Name: "token",
|
||
Value: req.Token,
|
||
Path: "/"},
|
||
}
|
||
} else {
|
||
cookies = []*http.Cookie{}
|
||
}
|
||
wk := ckwk.NewWK(req.Username, req.Password, req.Host, cookies)
|
||
if wk == nil {
|
||
ctx.JSON(200, dto.Error(-1, "登录失败:请提供账号密码或有效的 Token,并确保 Host 正确"))
|
||
return
|
||
}
|
||
if req.Token == "" {
|
||
ok, err := wk.Login()
|
||
if err != nil {
|
||
ctx.JSON(200, dto.Error(-1, err.Error()))
|
||
return
|
||
}
|
||
if !ok {
|
||
ctx.JSON(200, dto.Error(-1, "登录失败"))
|
||
return
|
||
}
|
||
}
|
||
|
||
sessionID := h.Session.Store(wk)
|
||
|
||
ctx.JSON(200, dto.Success(map[string]any{
|
||
"session_id": sessionID,
|
||
}))
|
||
}
|
||
|
||
func (h *WKHandler) Online(ctx *gin.Context) {
|
||
wk, ok := getWKFromContext(ctx)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
flag, err := wk.Online()
|
||
if err != nil {
|
||
if errors.Is(err, ckwk.ErrSessionRemoved) {
|
||
ctx.JSON(http.StatusUnauthorized, dto.Error(401, err.Error()))
|
||
return
|
||
}
|
||
ctx.JSON(200, dto.Error(-1, err.Error()))
|
||
return
|
||
}
|
||
|
||
if !flag {
|
||
ctx.JSON(200, dto.Error(-1, "保持账号状态失败"))
|
||
return
|
||
}
|
||
ctx.JSON(200, dto.Ok())
|
||
}
|
||
|
||
func (h *WKHandler) Logout(ctx *gin.Context) {
|
||
sessionID := ctx.GetString("session_id")
|
||
h.Session.Del(sessionID)
|
||
ctx.JSON(200, dto.Ok())
|
||
}
|
||
|
||
func (h *WKHandler) UserInfo(ctx *gin.Context) {
|
||
wk, ok := getWKFromContext(ctx)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
userinfo, err := wk.UserInfoGet()
|
||
if err != nil {
|
||
ctx.JSON(200, dto.Error(-1, err.Error()))
|
||
return
|
||
}
|
||
|
||
ctx.JSON(200, dto.Success(map[string]any{
|
||
"user": userinfo,
|
||
}))
|
||
}
|
||
|
||
func (h *WKHandler) Course(ctx *gin.Context) {
|
||
var req dto.CourseReq
|
||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||
ctx.JSON(200, dto.Error(-1, "请求参数错误"))
|
||
return
|
||
}
|
||
|
||
wk, ok := getWKFromContext(ctx)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
courses, err := wk.CourseGet(req.Status)
|
||
if err != nil {
|
||
ctx.JSON(200, dto.Error(-1, err.Error()))
|
||
return
|
||
}
|
||
|
||
ctx.JSON(200, dto.Success(map[string]any{
|
||
"courses": courses,
|
||
}))
|
||
}
|
||
|
||
func (h *WKHandler) Study(ctx *gin.Context) {
|
||
wk, ok := getWKFromContext(ctx)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
var req dto.StudyReq
|
||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||
ctx.JSON(200, dto.Error(-1, "请求参数错误"))
|
||
return
|
||
}
|
||
|
||
result, err := wk.Study(req.NodeID, req.StudyID, req.StudyTime, req.Status)
|
||
if err != nil {
|
||
if errors.Is(err, ckwk.ErrSessionRemoved) {
|
||
ctx.JSON(http.StatusUnauthorized, dto.Error(401, err.Error()))
|
||
return
|
||
}
|
||
ctx.JSON(200, dto.Error(-1, err.Error()))
|
||
return
|
||
}
|
||
|
||
ctx.JSON(200, dto.Success(result))
|
||
}
|
||
|
||
func (h *WKHandler) AllRecord(ctx *gin.Context) {
|
||
wk, ok := getWKFromContext(ctx)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
var req dto.AllRecordReq
|
||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||
ctx.JSON(200, dto.Error(-1, "请求参数错误"))
|
||
return
|
||
}
|
||
if req.Page < 1 {
|
||
req.Page = 1
|
||
}
|
||
var list any
|
||
var pageInfo ckwk.PageInfo
|
||
var err error
|
||
|
||
// 根据类型调用不同的泛型方法
|
||
switch req.RecordType {
|
||
case ckwk.RecordStudy:
|
||
res, e := wk.GetStudyList(req.CourseID, fmt.Sprint(req.Page))
|
||
err = e
|
||
if e == nil {
|
||
list, pageInfo = res.List, res.PageInfo
|
||
}
|
||
case ckwk.RecordWork:
|
||
res, e := wk.GetWorkList(req.CourseID, fmt.Sprint(req.Page))
|
||
err = e
|
||
if e == nil {
|
||
list, pageInfo = res.List, res.PageInfo
|
||
}
|
||
case ckwk.RecordExam:
|
||
res, e := wk.GetExamList(req.CourseID, fmt.Sprint(req.Page))
|
||
err = e
|
||
if e == nil {
|
||
list, pageInfo = res.List, res.PageInfo
|
||
}
|
||
case ckwk.RecordDiscuss:
|
||
res, e := wk.GetDiscussList(req.CourseID, fmt.Sprint(req.Page))
|
||
err = e
|
||
if e == nil {
|
||
list, pageInfo = res.List, res.PageInfo
|
||
}
|
||
default:
|
||
ctx.JSON(200, dto.Error(-1, "不支持的记录类型"))
|
||
return
|
||
}
|
||
if err != nil {
|
||
ctx.JSON(200, dto.Error(-1, err.Error()))
|
||
return
|
||
}
|
||
|
||
ctx.JSON(200, dto.Success(map[string]any{
|
||
"list": list,
|
||
"page_info": map[string]any{
|
||
"page": pageInfo.Page,
|
||
"pageSize": pageInfo.PageSize,
|
||
},
|
||
}))
|
||
}
|
||
|
||
func (h *WKHandler) Host(ctx *gin.Context) {
|
||
ctx.JSON(200, dto.Success(map[string]any{
|
||
"list": []map[string]any{
|
||
{"host": "cqcst.leykeji.com", "name": "劳动课程测评考试平台"},
|
||
{"host": "cqcst.zjxkeji.com", "name": "公益课程平台"},
|
||
{"host": "cqcst.suwankj.com", "name": "在线课程测评考试平台"},
|
||
{"host": "cqcst.yuruixxkj.com", "name": "在线测评考试平台"},
|
||
// {"host": "cqcst.yuncanjykeji.com", "name": "劳动课程测评考试平台"},
|
||
// {"host": "cqcst.yuruixxkj.com", "name": "公益课程平台"},
|
||
},
|
||
}))
|
||
}
|