Files
schedule_converter/main.go

207 lines
4.4 KiB
Go
Raw Normal View History

2026-03-13 20:43:44 +08:00
package main
import (
"context"
"flag"
"io"
2026-03-13 20:43:44 +08:00
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"jwts/auth"
"jwts/captcha"
"jwts/client"
"jwts/config"
grpcClient "jwts/grpc"
"jwts/parser"
"jwts/recognizer"
2026-03-13 20:43:44 +08:00
"github.com/gin-gonic/gin"
)
func main() {
mode := flag.String("mode", "http", "运行模式: http 或 grpc")
grpcAddr := flag.String("grpc-addr", "localhost:50051", "gRPC 服务器地址")
flag.Parse()
if *mode == "grpc" {
runGRPCClient(*grpcAddr)
} else {
runHTTPServer()
}
}
// runGRPCClient 运行 gRPC 客户端模式
func runGRPCClient(serverAddr string) {
runnerID := os.Getenv("RUNNER_ID")
if runnerID == "" {
hostname, _ := os.Hostname()
runnerID = "runner-" + hostname
}
version := os.Getenv("RUNNER_VERSION")
if version == "" {
version = "1.0.0"
}
log.Printf("启动 gRPC 客户端, Runner ID: %s, 版本: %s", runnerID, version)
client := grpcClient.NewClient(serverAddr, runnerID, version)
handler := grpcClient.NewTaskHandler()
client.SetHandler(handler)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 处理信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
if err := client.Connect(ctx); err != nil {
log.Printf("连接失败: %v", err)
os.Exit(1)
}
go func() {
<-sigChan
log.Println("正在关闭...")
client.Close()
cancel()
}()
// 启动心跳
go client.HeartbeatLoop(ctx, 30*time.Second)
// 运行主循环
if err := client.Run(ctx); err != nil {
log.Printf("客户端错误: %v", err)
}
}
// runHTTPServer 运行 HTTP 服务器模式
func runHTTPServer() {
// 初始化客户端
client.InitClient()
r := gin.Default()
// 健康检查接口
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
// 课表图片识别接口
r.POST("/recognize", func(c *gin.Context) {
// 读取上传的图片文件
file, header, err := c.Request.FormFile("image")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "请上传图片文件", "details": err.Error()})
return
}
defer file.Close()
log.Printf("收到图片上传: %s, 大小: %d bytes", header.Filename, header.Size)
// 读取图片数据
imageData, err := io.ReadAll(file)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "读取图片失败", "details": err.Error()})
return
}
// 调用识别模块
courses, err := recognizer.RecognizeFromBytes(imageData)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "识别失败", "details": err.Error()})
return
}
// 打印识别结果
recognizer.PrintCourses(courses)
// 保存到JSON文件
err = recognizer.SaveToJSON(courses, "schedule_result.json")
if err != nil {
log.Printf("保存JSON失败: %v", err)
}
c.JSON(http.StatusOK, gin.H{
"message": "识别成功",
"courses": courses,
"count": len(courses),
})
})
2026-03-13 20:43:44 +08:00
// 任务处理接口
r.POST("/task", func(c *gin.Context) {
// 这里可以根据请求体参数决定执行什么任务
// 目前默认执行原有的登录和获取课表逻辑
if config.Username == "" || config.Password == "" {
c.JSON(http.StatusInternalServerError, gin.H{"error": "环境变量未设置"})
return
}
// 执行任务
err := runTask()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "任务执行成功"})
})
// 启动服务
log.Println("Server starting on :8080")
if err := r.Run(":8080"); err != nil {
log.Fatalf("Failed to run server: %v", err)
}
}
func runTask() error {
// 步骤1: 获取初始Cookie
err := client.GetInitialCookies()
if err != nil {
log.Printf("获取Cookie失败: %v", err)
return err
}
// 重试登录
maxRetries := 3
for attempt := 1; attempt <= maxRetries; attempt++ {
log.Printf("尝试登录 (第%d次)...", attempt)
// 获取并识别验证码
captchaCode, err := captcha.GetAndRecognize(client.Client)
if err != nil {
log.Printf(" 验证码获取失败: %v", err)
continue
}
// 登录
success, err := auth.Login(captchaCode)
if err != nil {
log.Printf(" 登录失败: %v", err)
continue
}
if success {
// 获取课表
_, err = parser.FetchSchedule(client.Client)
if err != nil {
log.Printf("获取课表失败: %v", err)
}
return nil
}
log.Println(" 登录失败3秒后重试...")
}
return nil
}