h_exam.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. package handler
  2. import (
  3. webServerEntity "cicv-data-closedloop/amd64/web_server/entity"
  4. "cicv-data-closedloop/common/config/c_db"
  5. "cicv-data-closedloop/common/config/c_log"
  6. commonEntity "cicv-data-closedloop/common/entity"
  7. "cicv-data-closedloop/common/util"
  8. "github.com/gin-gonic/gin"
  9. "github.com/signintech/gopdf"
  10. "io"
  11. "log"
  12. "math"
  13. "net/http"
  14. "os"
  15. "sync"
  16. "time"
  17. )
  18. var (
  19. defaultTime = time.Date(2006, time.January, 2, 15, 4, 5, 0, time.Local)
  20. cacheMutex sync.Mutex
  21. cacheTeamName = make(map[string]time.Time)
  22. heartBeatTimeThreshold = 5 * time.Second // 心跳时间
  23. InitialPositionX = 0.00 // todo 需要比赛确认起点
  24. InitialPositionY = 0.00 // todo 需要比赛确认起点
  25. // todo 比赛阶段
  26. trialBegin = time.Date(2006, time.January, 2, 15, 4, 5, 0, time.Local)
  27. )
  28. // 考试心跳
  29. func Tick(c *gin.Context) {
  30. param := new(webServerEntity.ExamPao)
  31. // 映射到结构体
  32. if err := c.ShouldBindJSON(&param); err != nil {
  33. c_log.GlobalLogger.Error("请求体解析失败,错误信息为:", err)
  34. c.JSON(http.StatusBadRequest, commonEntity.Response{
  35. Code: 500,
  36. Msg: "请求体解析失败。",
  37. })
  38. return
  39. }
  40. teamName := param.TeamName
  41. positionX := param.PositionX
  42. positionY := param.PositionY
  43. if !util.ContainsKey(cacheTeamName, teamName) && math.Abs(positionX-InitialPositionX) < 5.00 && math.Abs(positionY-InitialPositionY) < 5.00 { // (在起点开始)
  44. sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-insert-begin_time-by-team_name.sql"])
  45. c_log.GlobalLogger.Info("保存比赛开始时间", sqlTemplate)
  46. if err := c_db.DoTx(sqlTemplate, []any{
  47. param.TeamName,
  48. time.Now(),
  49. }); err != nil {
  50. c_log.GlobalLogger.Error("保存比赛开始时间报错:", err)
  51. c.JSON(http.StatusBadRequest, commonEntity.Response{
  52. Code: 500,
  53. Msg: "保存比赛开始时间报错。",
  54. })
  55. return
  56. }
  57. } else if !util.ContainsKey(cacheTeamName, teamName) && math.Abs(positionX-InitialPositionX) < 5.00 && math.Abs(positionY-InitialPositionY) < 5.00 { // 不在起点(开始)
  58. // 车辆不是在起点启动的自动驾驶模式
  59. selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"])
  60. if err != nil {
  61. c_log.GlobalLogger.Error("读取sql文件报错:", err)
  62. return
  63. }
  64. // 可以传参数
  65. var result []webServerEntity.ExamPo
  66. if err = c_db.MysqlDb.Select(&result, selectSql, teamName); err != nil {
  67. c_log.GlobalLogger.Error("数据库查询报错:", err)
  68. return
  69. }
  70. if len(result) == 1 {
  71. c_log.GlobalLogger.Info("上一条数据记录为:", result[0])
  72. }
  73. // 添加队伍名到缓存中
  74. cacheTeamName[teamName] = time.Now()
  75. // 更新记录结束时间为默认时间
  76. sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-team_name.sql"])
  77. if err = c_db.DoTx(sqlTemplate, []any{
  78. defaultTime,
  79. teamName,
  80. }); err != nil {
  81. c_log.GlobalLogger.Error("插入数据报错:", err)
  82. return
  83. }
  84. c_log.GlobalLogger.Infof("队伍 %v 的考试在中途中断后重新开始。", teamName)
  85. } else if util.ContainsKey(cacheTeamName, teamName) { // 进行中
  86. cacheTeamName[teamName] = time.Now()
  87. }
  88. c.JSON(http.StatusOK, commonEntity.Response{
  89. Code: 200,
  90. Msg: "心跳接收成功。",
  91. })
  92. }
  93. func ExamEndTicker() {
  94. // 创建一个定时器,每隔一秒触发一次
  95. ticker := time.NewTicker(1 * time.Second)
  96. for {
  97. select {
  98. // 定时器触发时执行的代码
  99. case <-ticker.C:
  100. cacheMutex.Lock()
  101. {
  102. var keysToDelete []string
  103. for teamName, heartBeatTime := range cacheTeamName {
  104. if time.Since(heartBeatTime) > heartBeatTimeThreshold { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名
  105. keysToDelete = append(keysToDelete, teamName)
  106. }
  107. }
  108. for _, teamName := range keysToDelete { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名
  109. delete(cacheTeamName, teamName)
  110. // 1 查询指定队伍的开始时间最新的考试是否有结束时间,如果有则不在处理,如果没有则更新
  111. var result []webServerEntity.ExamPo
  112. selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"])
  113. if err != nil {
  114. c_log.GlobalLogger.Error("读取sql文件报错:", err)
  115. return
  116. }
  117. // 可以传参数
  118. if err = c_db.MysqlDb.Select(&result, selectSql, teamName); err != nil {
  119. c_log.GlobalLogger.Error("数据库查询报错:", err)
  120. return
  121. }
  122. c_log.GlobalLogger.Info("数据库查询成功:", result)
  123. if !result[0].EndTime.Equal(defaultTime) {
  124. c_log.GlobalLogger.Error("赛队", teamName, "考试已结束!")
  125. return
  126. }
  127. // 更新到数据库
  128. sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-team_name.sql"])
  129. if err := c_db.DoTx(sqlTemplate, []any{
  130. time.Now(),
  131. teamName,
  132. }); err != nil {
  133. c_log.GlobalLogger.Error("插入数据报错:", err)
  134. return
  135. }
  136. c_log.GlobalLogger.Infof("队伍 %v 的考试结束。", teamName)
  137. }
  138. }
  139. cacheMutex.Unlock()
  140. }
  141. }
  142. }
  143. // 考试开始时间
  144. func Begin(c *gin.Context) {
  145. param := new(webServerEntity.ExamPao)
  146. // 映射到结构体
  147. if err := c.ShouldBindJSON(&param); err != nil {
  148. c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err)
  149. c.JSON(http.StatusBadRequest, commonEntity.Response{
  150. Code: 500,
  151. Msg: "请求体解析失败。",
  152. })
  153. return
  154. }
  155. // 插入到数据库
  156. sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-insert-begin_time-by-team_name.sql"])
  157. c_log.GlobalLogger.Info("插入比赛开始时间", sqlTemplate)
  158. if err := c_db.DoTx(sqlTemplate, []any{
  159. param.TeamName,
  160. time.Now(),
  161. }); err != nil {
  162. c_log.GlobalLogger.Error("插入数据报错:", err)
  163. c.JSON(http.StatusBadRequest, commonEntity.Response{
  164. Code: 500,
  165. Msg: "插入数据报错。",
  166. })
  167. return
  168. }
  169. c.JSON(http.StatusOK, commonEntity.Response{
  170. Code: 200,
  171. Msg: "插入数据成功。",
  172. })
  173. }
  174. // 考试结束时间
  175. func End(c *gin.Context) {
  176. param := new(webServerEntity.ExamPao)
  177. // 映射到结构体
  178. if err := c.ShouldBindJSON(&param); err != nil {
  179. c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err)
  180. c.JSON(http.StatusBadRequest, commonEntity.Response{
  181. Code: 500,
  182. Msg: "请求体解析失败。",
  183. })
  184. return
  185. }
  186. // 1 查询指定队伍的开始时间最新的考试是否有结束时间,如果有则不在处理,如果没有则更新
  187. var result []webServerEntity.ExamPo
  188. selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"])
  189. if err != nil {
  190. c_log.GlobalLogger.Error("读取sql文件报错:", err)
  191. c.JSON(http.StatusBadRequest, commonEntity.Response{
  192. Code: 500,
  193. Msg: "读取sql文件报错。",
  194. })
  195. return
  196. }
  197. // 可以传参数
  198. if err = c_db.MysqlDb.Select(&result, selectSql, param.TeamName); err != nil {
  199. c_log.GlobalLogger.Error("数据库查询报错:", err)
  200. c.JSON(http.StatusBadRequest, commonEntity.Response{
  201. Code: 500,
  202. Msg: "数据库查询报错。",
  203. })
  204. return
  205. }
  206. c_log.GlobalLogger.Info("数据库查询成功:", result)
  207. if !result[0].EndTime.Equal(defaultTime) {
  208. c_log.GlobalLogger.Error("赛队", param.TeamName, "重复请求考试结束接口!")
  209. c.JSON(http.StatusBadRequest, commonEntity.Response{
  210. Code: 500,
  211. Msg: "重复请求。",
  212. })
  213. return
  214. }
  215. // 更新到数据库
  216. sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-team_name.sql"])
  217. if err := c_db.DoTx(sqlTemplate, []any{
  218. time.Now(),
  219. param.TeamName,
  220. }); err != nil {
  221. c_log.GlobalLogger.Error("插入数据报错:", err)
  222. c.JSON(http.StatusBadRequest, commonEntity.Response{
  223. Code: 500,
  224. Msg: "插入数据报错。",
  225. })
  226. return
  227. }
  228. c.JSON(http.StatusOK, commonEntity.Response{
  229. Code: 200,
  230. Msg: "插入数据成功。",
  231. })
  232. }
  233. // 分页查询
  234. // todo 如果日期为默认值,则返回空""
  235. func Page(c *gin.Context) {
  236. param := new(webServerEntity.ExamPagePao)
  237. _ = c.ShouldBindJSON(&param)
  238. var resultPos []webServerEntity.ExamPo
  239. var resultPosTotal []int
  240. var pageSql string
  241. var totalSql string
  242. offset := (param.CurrentPage - 1) * param.PageSize
  243. size := param.PageSize
  244. if param.TeamName == "" && param.Topic == "" {
  245. pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page.sql"])
  246. totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total.sql"])
  247. err := c_db.MysqlDb.Select(&resultPos, pageSql, offset, size)
  248. if err != nil {
  249. c_log.GlobalLogger.Error(err)
  250. }
  251. err = c_db.MysqlDb.Select(&resultPosTotal, totalSql)
  252. if err != nil {
  253. c_log.GlobalLogger.Error(err)
  254. }
  255. }
  256. if param.TeamName != "" && param.Topic == "" {
  257. pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-team_name.sql"])
  258. totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-team_name.sql"])
  259. _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.TeamName+"%", offset, size)
  260. _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.TeamName+"%")
  261. }
  262. if param.TeamName == "" && param.Topic != "" {
  263. pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-topic.sql"])
  264. totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-topic.sql"])
  265. _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.Topic+"%", offset, size)
  266. _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.Topic+"%")
  267. }
  268. if param.TeamName != "" && param.Topic != "" {
  269. pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-team_name-and-topic.sql"])
  270. totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-team_name-and-topic.sql"])
  271. _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.TeamName+"%", "%"+param.Topic+"%", offset, size)
  272. _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.TeamName+"%", "%"+param.Topic+"%")
  273. }
  274. var resultVos []webServerEntity.ExamVo
  275. for _, po := range resultPos {
  276. resultVos = append(resultVos, webServerEntity.ExamVo{
  277. Id: po.Id,
  278. TeamName: po.TeamName,
  279. Topic: po.Topic,
  280. BeginTime: util.GetTimeString(po.BeginTime),
  281. EndTime: util.GetTimeString(po.EndTime),
  282. ScoreOnline: po.ScoreOnline,
  283. ScoreOffline: po.ScoreOffline,
  284. ScoreFinal: po.ScoreFinal,
  285. Details: po.Details,
  286. ScoreReportPath: po.ScoreReportPath,
  287. })
  288. }
  289. c.JSON(http.StatusOK, commonEntity.Response{
  290. Code: 200,
  291. Msg: "分页查询成功!",
  292. Data: resultVos,
  293. Total: resultPosTotal[0],
  294. })
  295. }
  296. // 评分报告pdf下载
  297. func Report(c *gin.Context) {
  298. //param := new(webServerEntity.ExamReportPao)
  299. //// 映射到结构体
  300. //if err := c.ShouldBindJSON(&param); err != nil {
  301. // c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err)
  302. // c.JSON(http.StatusBadRequest, commonEntity.Response{
  303. // Code: 500,
  304. // Msg: "请求体解析失败。",
  305. // })
  306. // return
  307. //}
  308. // 1 根据ID查询数据
  309. // 2 根据数据生成pdf
  310. // 3 创建pdf文件
  311. {
  312. // 1 初始化 pdf 对象
  313. pdf := gopdf.GoPdf{}
  314. pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})
  315. // 2 添加一页
  316. pdf.AddPage()
  317. // 3
  318. err := pdf.AddTTFFont("simfang", "D:\\code\\cicv-data-closedloop\\test\\pdf\\ttf\\simfang.ttf")
  319. if err != nil {
  320. log.Print(err.Error())
  321. return
  322. }
  323. err = pdf.SetFont("simfang", "", 14)
  324. if err != nil {
  325. log.Print(err.Error())
  326. return
  327. }
  328. err = pdf.Cell(nil, "您好")
  329. if err != nil {
  330. return
  331. }
  332. err = pdf.WritePdf("D:\\hello.pdf")
  333. if err != nil {
  334. return
  335. }
  336. }
  337. // 打开要发送的文件
  338. file, err := os.Open("D:\\hello.pdf")
  339. if err != nil {
  340. c.String(http.StatusNotFound, "文件未找到")
  341. return
  342. }
  343. defer file.Close()
  344. // 将文件复制到响应主体
  345. _, err = io.Copy(c.Writer, file)
  346. if err != nil {
  347. c.String(http.StatusInternalServerError, "无法复制文件到响应体")
  348. return
  349. }
  350. }