package handler import ( webServerEntity "cicv-data-closedloop/amd64/web_server/entity" "cicv-data-closedloop/common/config/c_db" "cicv-data-closedloop/common/config/c_log" commonEntity "cicv-data-closedloop/common/entity" "cicv-data-closedloop/common/util" "github.com/gin-gonic/gin" "github.com/signintech/gopdf" "io" "log" "math" "net/http" "os" "sync" "time" ) var ( defaultTime = time.Date(2006, time.January, 2, 15, 4, 5, 0, time.Local) cacheMutex sync.Mutex cacheTeamName = make(map[string]time.Time) heartBeatTimeThreshold = 5 * time.Second // 心跳时间 InitialPositionX = 0.00 // todo 需要比赛确认起点 InitialPositionY = 0.00 // todo 需要比赛确认起点 // todo 比赛阶段 trialBegin = time.Date(2006, time.January, 2, 15, 4, 5, 0, time.Local) ) // 考试心跳 func Tick(c *gin.Context) { param := new(webServerEntity.ExamPao) // 映射到结构体 if err := c.ShouldBindJSON(¶m); err != nil { c_log.GlobalLogger.Error("请求体解析失败,错误信息为:", err) c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "请求体解析失败。", }) return } teamName := param.TeamName positionX := param.PositionX positionY := param.PositionY if !util.ContainsKey(cacheTeamName, teamName) && math.Abs(positionX-InitialPositionX) < 5.00 && math.Abs(positionY-InitialPositionY) < 5.00 { // (在起点开始) sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-insert-begin_time-by-team_name.sql"]) c_log.GlobalLogger.Info("保存比赛开始时间", sqlTemplate) if err := c_db.DoTx(sqlTemplate, []any{ param.TeamName, time.Now(), }); err != nil { c_log.GlobalLogger.Error("保存比赛开始时间报错:", err) c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "保存比赛开始时间报错。", }) return } } else if !util.ContainsKey(cacheTeamName, teamName) && math.Abs(positionX-InitialPositionX) < 5.00 && math.Abs(positionY-InitialPositionY) < 5.00 { // 不在起点(开始) // 车辆不是在起点启动的自动驾驶模式 selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"]) if err != nil { c_log.GlobalLogger.Error("读取sql文件报错:", err) return } // 可以传参数 var result []webServerEntity.ExamPo if err = c_db.MysqlDb.Select(&result, selectSql, teamName); err != nil { c_log.GlobalLogger.Error("数据库查询报错:", err) return } if len(result) == 1 { c_log.GlobalLogger.Info("上一条数据记录为:", result[0]) } // 添加队伍名到缓存中 cacheTeamName[teamName] = time.Now() // 更新记录结束时间为默认时间 sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-team_name.sql"]) if err = c_db.DoTx(sqlTemplate, []any{ defaultTime, teamName, }); err != nil { c_log.GlobalLogger.Error("插入数据报错:", err) return } c_log.GlobalLogger.Infof("队伍 %v 的考试在中途中断后重新开始。", teamName) } else if util.ContainsKey(cacheTeamName, teamName) { // 进行中 cacheTeamName[teamName] = time.Now() } c.JSON(http.StatusOK, commonEntity.Response{ Code: 200, Msg: "心跳接收成功。", }) } func ExamEndTicker() { // 创建一个定时器,每隔一秒触发一次 ticker := time.NewTicker(1 * time.Second) for { select { // 定时器触发时执行的代码 case <-ticker.C: cacheMutex.Lock() { var keysToDelete []string for teamName, heartBeatTime := range cacheTeamName { if time.Since(heartBeatTime) > heartBeatTimeThreshold { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名 keysToDelete = append(keysToDelete, teamName) } } for _, teamName := range keysToDelete { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名 delete(cacheTeamName, teamName) // 1 查询指定队伍的开始时间最新的考试是否有结束时间,如果有则不在处理,如果没有则更新 var result []webServerEntity.ExamPo selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"]) if err != nil { c_log.GlobalLogger.Error("读取sql文件报错:", err) return } // 可以传参数 if err = c_db.MysqlDb.Select(&result, selectSql, teamName); err != nil { c_log.GlobalLogger.Error("数据库查询报错:", err) return } c_log.GlobalLogger.Info("数据库查询成功:", result) if !result[0].EndTime.Equal(defaultTime) { c_log.GlobalLogger.Error("赛队", teamName, "考试已结束!") return } // 更新到数据库 sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-team_name.sql"]) if err := c_db.DoTx(sqlTemplate, []any{ time.Now(), teamName, }); err != nil { c_log.GlobalLogger.Error("插入数据报错:", err) return } c_log.GlobalLogger.Infof("队伍 %v 的考试结束。", teamName) } } cacheMutex.Unlock() } } } // 考试开始时间 func Begin(c *gin.Context) { param := new(webServerEntity.ExamPao) // 映射到结构体 if err := c.ShouldBindJSON(¶m); err != nil { c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err) c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "请求体解析失败。", }) return } // 插入到数据库 sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-insert-begin_time-by-team_name.sql"]) c_log.GlobalLogger.Info("插入比赛开始时间", sqlTemplate) if err := c_db.DoTx(sqlTemplate, []any{ param.TeamName, time.Now(), }); err != nil { c_log.GlobalLogger.Error("插入数据报错:", err) c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "插入数据报错。", }) return } c.JSON(http.StatusOK, commonEntity.Response{ Code: 200, Msg: "插入数据成功。", }) } // 考试结束时间 func End(c *gin.Context) { param := new(webServerEntity.ExamPao) // 映射到结构体 if err := c.ShouldBindJSON(¶m); err != nil { c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err) c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "请求体解析失败。", }) return } // 1 查询指定队伍的开始时间最新的考试是否有结束时间,如果有则不在处理,如果没有则更新 var result []webServerEntity.ExamPo selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"]) if err != nil { c_log.GlobalLogger.Error("读取sql文件报错:", err) c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "读取sql文件报错。", }) return } // 可以传参数 if err = c_db.MysqlDb.Select(&result, selectSql, param.TeamName); err != nil { c_log.GlobalLogger.Error("数据库查询报错:", err) c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "数据库查询报错。", }) return } c_log.GlobalLogger.Info("数据库查询成功:", result) if !result[0].EndTime.Equal(defaultTime) { c_log.GlobalLogger.Error("赛队", param.TeamName, "重复请求考试结束接口!") c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "重复请求。", }) return } // 更新到数据库 sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-team_name.sql"]) if err := c_db.DoTx(sqlTemplate, []any{ time.Now(), param.TeamName, }); err != nil { c_log.GlobalLogger.Error("插入数据报错:", err) c.JSON(http.StatusBadRequest, commonEntity.Response{ Code: 500, Msg: "插入数据报错。", }) return } c.JSON(http.StatusOK, commonEntity.Response{ Code: 200, Msg: "插入数据成功。", }) } // 分页查询 // todo 如果日期为默认值,则返回空"" func Page(c *gin.Context) { param := new(webServerEntity.ExamPagePao) _ = c.ShouldBindJSON(¶m) var resultPos []webServerEntity.ExamPo var resultPosTotal []int var pageSql string var totalSql string offset := (param.CurrentPage - 1) * param.PageSize size := param.PageSize if param.TeamName == "" && param.Topic == "" { pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page.sql"]) totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total.sql"]) err := c_db.MysqlDb.Select(&resultPos, pageSql, offset, size) if err != nil { c_log.GlobalLogger.Error(err) } err = c_db.MysqlDb.Select(&resultPosTotal, totalSql) if err != nil { c_log.GlobalLogger.Error(err) } } if param.TeamName != "" && param.Topic == "" { pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-team_name.sql"]) totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-team_name.sql"]) _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.TeamName+"%", offset, size) _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.TeamName+"%") } if param.TeamName == "" && param.Topic != "" { pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-topic.sql"]) totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-topic.sql"]) _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.Topic+"%", offset, size) _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.Topic+"%") } if param.TeamName != "" && param.Topic != "" { pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-team_name-and-topic.sql"]) totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-team_name-and-topic.sql"]) _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.TeamName+"%", "%"+param.Topic+"%", offset, size) _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.TeamName+"%", "%"+param.Topic+"%") } var resultVos []webServerEntity.ExamVo for _, po := range resultPos { resultVos = append(resultVos, webServerEntity.ExamVo{ Id: po.Id, TeamName: po.TeamName, Topic: po.Topic, BeginTime: util.GetTimeString(po.BeginTime), EndTime: util.GetTimeString(po.EndTime), ScoreOnline: po.ScoreOnline, ScoreOffline: po.ScoreOffline, ScoreFinal: po.ScoreFinal, Details: po.Details, ScoreReportPath: po.ScoreReportPath, }) } c.JSON(http.StatusOK, commonEntity.Response{ Code: 200, Msg: "分页查询成功!", Data: resultVos, Total: resultPosTotal[0], }) } // 评分报告pdf下载 func Report(c *gin.Context) { //param := new(webServerEntity.ExamReportPao) //// 映射到结构体 //if err := c.ShouldBindJSON(¶m); err != nil { // c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err) // c.JSON(http.StatusBadRequest, commonEntity.Response{ // Code: 500, // Msg: "请求体解析失败。", // }) // return //} // 1 根据ID查询数据 // 2 根据数据生成pdf // 3 创建pdf文件 { // 1 初始化 pdf 对象 pdf := gopdf.GoPdf{} pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4}) // 2 添加一页 pdf.AddPage() // 3 err := pdf.AddTTFFont("simfang", "D:\\code\\cicv-data-closedloop\\test\\pdf\\ttf\\simfang.ttf") if err != nil { log.Print(err.Error()) return } err = pdf.SetFont("simfang", "", 14) if err != nil { log.Print(err.Error()) return } err = pdf.Cell(nil, "您好") if err != nil { return } err = pdf.WritePdf("D:\\hello.pdf") if err != nil { return } } // 打开要发送的文件 file, err := os.Open("D:\\hello.pdf") if err != nil { c.String(http.StatusNotFound, "文件未找到") return } defer file.Close() // 将文件复制到响应主体 _, err = io.Copy(c.Writer, file) if err != nil { c.String(http.StatusInternalServerError, "无法复制文件到响应体") return } }