From b0db64bd7b3868df2488ae65d40d5777c2522e53 Mon Sep 17 00:00:00 2001 From: zhilv Date: Thu, 26 Mar 2026 22:59:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现定时任务 --- go.mod | 3 +++ go.sum | 6 +++++ internal/ckwk/api.go | 3 ++- internal/ckwk/session_manager.go | 41 ++++++++++++++++++++++++++++++++ internal/conf/const.go | 5 ++++ internal/handler/ckwk.go | 28 ++++++++++++++++++---- internal/router/router.go | 17 ++++++++++--- internal/schedule/schedule.go | 32 +++++++++++++++++++++++++ 8 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 internal/conf/const.go create mode 100644 internal/schedule/schedule.go diff --git a/go.mod b/go.mod index 9ec329c..2836a83 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gin-contrib/cors v1.7.6 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect @@ -34,6 +35,8 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect + github.com/robfig/cron v1.2.0 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect diff --git a/go.sum b/go.sum index 4a2513f..09264be 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= +github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= @@ -60,6 +62,10 @@ github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/internal/ckwk/api.go b/internal/ckwk/api.go index 028e016..7769224 100644 --- a/internal/ckwk/api.go +++ b/internal/ckwk/api.go @@ -1,6 +1,7 @@ package ckwk import ( + "ckwk/internal/conf" "ckwk/pkg/common" "ckwk/pkg/log" "ckwk/pkg/request" @@ -97,7 +98,7 @@ func (wk *WK) Code() (string, error) { "png_fix": "false", }). SetResult(&result). - Post("http://localhost:8000/ocr") + Post(fmt.Sprintf("%s/ocr", conf.DdddOCR)) if err != nil { return "", fmt.Errorf("获取验证码验证结果失败: %w", err) } diff --git a/internal/ckwk/session_manager.go b/internal/ckwk/session_manager.go index 7fc976f..99a5505 100644 --- a/internal/ckwk/session_manager.go +++ b/internal/ckwk/session_manager.go @@ -115,3 +115,44 @@ func (m *SessionManager) KeepAlive(ctx context.Context, id string, wk *WK) { } } } + +func (m *SessionManager) ClearAll() { + m.mu.Lock() + defer m.mu.Unlock() + + for sessionID, item := range m.sessions { + // 停止 KeepAlive + if item.cancel != nil { + item.cancel() + } + + userKey := item.Instance.Host + ":" + item.Instance.Username + delete(m.userToSession, userKey) + delete(m.sessions, sessionID) + + log.Info("清理 Session", zap.String("id", sessionID)) + } + + log.Info("所有 Session 已清空") +} + +func (m *SessionManager) ClearExpired(d time.Duration) { + m.mu.Lock() + defer m.mu.Unlock() + + now := time.Now() + + for sessionID, item := range m.sessions { + if now.Sub(item.LastValue) > d { + if item.cancel != nil { + item.cancel() + } + + userKey := item.Instance.Host + ":" + item.Instance.Username + delete(m.userToSession, userKey) + delete(m.sessions, sessionID) + + log.Info("清理过期 Session", zap.String("id", sessionID)) + } + } +} diff --git a/internal/conf/const.go b/internal/conf/const.go new file mode 100644 index 0000000..aa10b4c --- /dev/null +++ b/internal/conf/const.go @@ -0,0 +1,5 @@ +package conf + +const ( + DdddOCR = "https://ocr.kmux.cn" +) diff --git a/internal/handler/ckwk.go b/internal/handler/ckwk.go index c087c34..248d5e5 100644 --- a/internal/handler/ckwk.go +++ b/internal/handler/ckwk.go @@ -28,11 +28,16 @@ func (h *WKHandler) Login(ctx *gin.Context) { return } log.Debug("请求数据", zap.Any("req", req)) - var cookies = []*http.Cookie{ - { - Name: "token", - Value: req.Token, - Path: "/"}, + 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 { @@ -171,3 +176,16 @@ func (h *WKHandler) AllRecord(ctx *gin.Context) { "page_info": pageInfo, })) } + +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": "公益课程平台"}, + }, + })) +} diff --git a/internal/router/router.go b/internal/router/router.go index fb2fab6..b7cc283 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -3,22 +3,33 @@ package router import ( "ckwk/internal/handler" "ckwk/internal/middleware" + "ckwk/internal/schedule" + "time" + "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func SetupRouter() *gin.Engine { r := gin.Default() + r.Use(cors.New(cors.Config{ + AllowOrigins: []string{"*://*", "http://localhost:5173"}, + AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH"}, + AllowHeaders: []string{"*", "X-Session-Id"}, + ExposeHeaders: []string{"Content-Length"}, + AllowCredentials: true, + MaxAge: 12 * time.Hour, + })) wkHandler := handler.NewWKHandler() sessionMiddleware := middleware.SessionMiddleware(wkHandler.Session) + schedule.StartCron(wkHandler.Session) api := r.Group("/api") { api.POST("/login", wkHandler.Login) - v1 := api.Group("/v1", sessionMiddleware) + v1 := api.Group("/v1") { - v1.POST("/online", wkHandler.Online) - v1.POST("/logout", wkHandler.Logout) + v1.GET("/host", wkHandler.Host) } v2 := api.Group("/v2", sessionMiddleware) diff --git a/internal/schedule/schedule.go b/internal/schedule/schedule.go new file mode 100644 index 0000000..f630138 --- /dev/null +++ b/internal/schedule/schedule.go @@ -0,0 +1,32 @@ +package schedule + +import ( + "ckwk/internal/ckwk" + "ckwk/pkg/log" + "time" + + "github.com/robfig/cron/v3" + + "go.uber.org/zap" +) + +func StartCron(m *ckwk.SessionManager) { + loc, _ := time.LoadLocation("Asia/Singapore") + + c := cron.New( + cron.WithLocation(loc), + ) + + // 每天 6 点执行 + _, err := c.AddFunc("0 2 * * *", func() { + log.Info("开始定时清理 Session") + m.ClearAll() + }) + + if err != nil { + log.Error("cron 添加任务失败", zap.Error(err)) + return + } + + c.Start() +}