feat: 添加定时系统

- 实现定时任务
This commit is contained in:
2026-03-26 22:59:58 +08:00
parent 858c29a799
commit b0db64bd7b
8 changed files with 126 additions and 9 deletions

3
go.mod
View File

@@ -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

6
go.sum
View File

@@ -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=

View File

@@ -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)
}

View File

@@ -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))
}
}
}

5
internal/conf/const.go Normal file
View File

@@ -0,0 +1,5 @@
package conf
const (
DdddOCR = "https://ocr.kmux.cn"
)

View File

@@ -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": "公益课程平台"},
},
}))
}

View File

@@ -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)

View File

@@ -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()
}