h_exam.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. package handler
  2. import (
  3. webServerEntity "cicv-data-closedloop/amd64/web_server/entity"
  4. "cicv-data-closedloop/amd64/web_server/infra"
  5. "cicv-data-closedloop/common/config/c_db"
  6. "cicv-data-closedloop/common/config/c_log"
  7. commonEntity "cicv-data-closedloop/common/entity"
  8. "cicv-data-closedloop/common/util"
  9. "encoding/json"
  10. "fmt"
  11. "github.com/gin-gonic/gin"
  12. "github.com/signintech/gopdf"
  13. "io"
  14. "log"
  15. "math"
  16. "net/http"
  17. "os"
  18. "strconv"
  19. "sync"
  20. "time"
  21. )
  22. var (
  23. defaultTime = time.Date(2006, time.January, 2, 15, 4, 5, 0, time.Local)
  24. cacheMutex sync.Mutex
  25. cacheTeamName = make(map[string]time.Time)
  26. heartBeatTimeThreshold = 5 * time.Second // 心跳时间
  27. InitialPositionX = 456292.00 // todo 需要比赛确认起点
  28. InitialPositionY = 4397957.00 // todo 需要比赛确认起点
  29. trialBegin = time.Date(2024, time.June, 16, 13, 00, 00, 0, time.Local)
  30. trialEnd = time.Date(2024, time.June, 19, 23, 59, 59, 0, time.Local)
  31. )
  32. // 考试心跳
  33. func Tick(c *gin.Context) {
  34. param := new(webServerEntity.ExamPao)
  35. // 映射到结构体
  36. if err := c.ShouldBindJSON(&param); err != nil {
  37. c_log.GlobalLogger.Error("请求体解析失败,错误信息为:", err)
  38. c.JSON(http.StatusBadRequest, commonEntity.Response{
  39. Code: 500,
  40. Msg: "请求体解析失败。",
  41. })
  42. return
  43. }
  44. teamName := param.TeamName
  45. positionX, _ := strconv.ParseFloat(param.PositionX, 64)
  46. var positionY float64
  47. numStr := param.PositionY
  48. _, _ = fmt.Sscanf(numStr, "%e", &positionY)
  49. if !util.ContainsKey(cacheTeamName, teamName) && math.Abs(positionX-InitialPositionX) < 5.00 && math.Abs(positionY-InitialPositionY) < 5.00 { // (在起点开始)
  50. sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-insert-begin_time-and-topic-and-equipment_no-by-team_name.sql"])
  51. stage := ""
  52. if time.Now().Before(trialBegin) {
  53. stage = "表演赛"
  54. } else if time.Now().After(trialBegin) && time.Now().Before(trialEnd) {
  55. stage = "预赛"
  56. } else {
  57. stage = "决赛"
  58. }
  59. competitionBegin := time.Now()
  60. cacheTeamName[teamName] = competitionBegin
  61. c_log.GlobalLogger.Infof("当前比赛阶段为 %v ,队伍 %v 在起始点范围内第一次启动,保存新一条比赛记录的开始时间 %v", stage, teamName, competitionBegin)
  62. if err := c_db.DoTx(sqlTemplate, []any{param.TeamName, stage, competitionBegin, param.EquipmentNo}); err != nil {
  63. c_log.GlobalLogger.Error("保存比赛开始时间报错:", err)
  64. c.JSON(http.StatusBadRequest, commonEntity.Response{Code: 500, Msg: "保存比赛开始时间报错。"})
  65. return
  66. }
  67. } else if !util.ContainsKey(cacheTeamName, teamName) && (math.Abs(positionX-InitialPositionX) > 5.00 || math.Abs(positionY-InitialPositionY) > 5.00) { // 不在起点(开始)
  68. // 车辆不是在起点启动的自动驾驶模式
  69. selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"])
  70. if err != nil {
  71. c_log.GlobalLogger.Error("读取sql文件报错:", err)
  72. return
  73. }
  74. // 可以传参数
  75. var result []webServerEntity.ExamPo
  76. if err = c_db.MysqlDb.Select(&result, selectSql, teamName); err != nil {
  77. c_log.GlobalLogger.Error("数据库查询报错:", err)
  78. return
  79. }
  80. if len(result) == 1 {
  81. c_log.GlobalLogger.Info("上一条数据记录为:", result[0])
  82. }
  83. // 添加队伍名到缓存中
  84. cacheTeamName[teamName] = time.Now()
  85. // 更新记录结束时间为默认时间
  86. sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-team_name.sql"])
  87. if err = c_db.DoTx(sqlTemplate, []any{
  88. defaultTime,
  89. teamName,
  90. }); err != nil {
  91. c_log.GlobalLogger.Error("插入数据报错:", err)
  92. return
  93. }
  94. c_log.GlobalLogger.Infof("队伍 %v 的考试在中途中断后重新开始。", teamName)
  95. } else if util.ContainsKey(cacheTeamName, teamName) { // 进行中
  96. cacheTeamName[teamName] = time.Now()
  97. c_log.GlobalLogger.Errorf("接收到心跳信息,队伍名字为 %s,x坐标为 %.f,y坐标为 %.f ", teamName, positionX, positionY)
  98. } else {
  99. c_log.GlobalLogger.Errorf("接收到心跳信息但考试并未开始,队伍名字为 %s,x坐标为 %.f,y坐标为 %.f ", teamName, positionX, positionY)
  100. }
  101. c.JSON(http.StatusOK, commonEntity.Response{
  102. Code: 200,
  103. Msg: "心跳接收成功。",
  104. })
  105. }
  106. func ExamEndTicker() {
  107. // 创建一个定时器,每隔一秒触发一次
  108. ticker := time.NewTicker(1 * time.Second)
  109. for {
  110. select {
  111. // 定时器触发时执行的代码
  112. case <-ticker.C:
  113. cacheMutex.Lock()
  114. {
  115. var keysToDelete []string
  116. for teamName, heartBeatTime := range cacheTeamName {
  117. if time.Since(heartBeatTime) > heartBeatTimeThreshold { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名
  118. c_log.GlobalLogger.Infof("队伍 %v 心跳超时,比赛结束。", teamName)
  119. keysToDelete = append(keysToDelete, teamName)
  120. }
  121. }
  122. for _, teamName := range keysToDelete { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名
  123. delete(cacheTeamName, teamName)
  124. // 1 查询指定队伍的开始时间最新的考试是否有结束时间,如果有则不在处理,如果没有则更新
  125. var result []webServerEntity.ExamPo
  126. selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"])
  127. if err != nil {
  128. c_log.GlobalLogger.Error("读取sql文件报错:", err)
  129. return
  130. }
  131. // 可以传参数
  132. if err = c_db.MysqlDb.Select(&result, selectSql, teamName); err != nil {
  133. c_log.GlobalLogger.Error("数据库查询报错:", err)
  134. return
  135. }
  136. if !result[0].EndTime.Equal(defaultTime) {
  137. c_log.GlobalLogger.Error("赛队", teamName, "考试已结束!")
  138. return
  139. }
  140. // 更新到数据库(只更新最新一条)
  141. stage := ""
  142. if time.Now().Before(trialBegin) {
  143. stage = "表演赛"
  144. } else if time.Now().After(trialBegin) && time.Now().Before(trialEnd) {
  145. stage = "预赛"
  146. } else {
  147. stage = "决赛"
  148. }
  149. // 查询最新一条的id
  150. selectSql, err = util.ReadFile(c_db.SqlFilesMap["exam-select-max-id-by-team_name-and-topic.sql"])
  151. if err != nil {
  152. c_log.GlobalLogger.Error("读取sql文件报错:", err)
  153. return
  154. }
  155. // 可以传参数
  156. var resultId []int
  157. if err = c_db.MysqlDb.Select(&resultId, selectSql, teamName, stage); err != nil {
  158. c_log.GlobalLogger.Error("数据库查询报错:", err)
  159. return
  160. }
  161. if len(resultId) == 1 {
  162. c_log.GlobalLogger.Info("更新数据结束时间,id为:", resultId)
  163. sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-id.sql"])
  164. if err := c_db.DoTx(sqlTemplate, []any{
  165. time.Now(),
  166. resultId[0],
  167. }); err != nil {
  168. c_log.GlobalLogger.Error("插入数据报错:", err)
  169. return
  170. }
  171. } else {
  172. c_log.GlobalLogger.Error("查询id失败:", resultId)
  173. }
  174. c_log.GlobalLogger.Infof("队伍 %v 的考试结束。", teamName)
  175. }
  176. }
  177. cacheMutex.Unlock()
  178. }
  179. }
  180. }
  181. //// 考试开始时间
  182. //func Begin(c *gin.Context) {
  183. // param := new(webServerEntity.ExamPao)
  184. // // 映射到结构体
  185. // if err := c.ShouldBindJSON(&param); err != nil {
  186. // c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err)
  187. // c.JSON(http.StatusBadRequest, commonEntity.Response{
  188. // Code: 500,
  189. // Msg: "请求体解析失败。",
  190. // })
  191. // return
  192. // }
  193. // // 插入到数据库
  194. // sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-insert-begin_time-by-team_name.sql"])
  195. // c_log.GlobalLogger.Info("插入比赛开始时间", sqlTemplate)
  196. // if err := c_db.DoTx(sqlTemplate, []any{
  197. // param.TeamName,
  198. // time.Now(),
  199. // }); err != nil {
  200. // c_log.GlobalLogger.Error("插入数据报错:", err)
  201. // c.JSON(http.StatusBadRequest, commonEntity.Response{
  202. // Code: 500,
  203. // Msg: "插入数据报错。",
  204. // })
  205. // return
  206. // }
  207. // c.JSON(http.StatusOK, commonEntity.Response{
  208. // Code: 200,
  209. // Msg: "插入数据成功。",
  210. // })
  211. //}
  212. //
  213. //// 考试结束时间
  214. //func End(c *gin.Context) {
  215. // param := new(webServerEntity.ExamPao)
  216. // // 映射到结构体
  217. // if err := c.ShouldBindJSON(&param); err != nil {
  218. // c_log.GlobalLogger.Error("接收请求参数报错:", err)
  219. // c.JSON(http.StatusBadRequest, commonEntity.Response{
  220. // Code: 500,
  221. // Msg: "请求体解析失败。",
  222. // })
  223. // return
  224. // }
  225. // // 1 查询指定队伍的开始时间最新的考试是否有结束时间,如果有则不在处理,如果没有则更新
  226. // var result []webServerEntity.ExamPo
  227. // selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-latest-by-team_name.sql"])
  228. // if err != nil {
  229. // c_log.GlobalLogger.Error("读取sql文件报错:", err)
  230. // c.JSON(http.StatusBadRequest, commonEntity.Response{
  231. // Code: 500,
  232. // Msg: "读取sql文件报错。",
  233. // })
  234. // return
  235. // }
  236. // // 可以传参数
  237. // if err = c_db.MysqlDb.Select(&result, selectSql, param.TeamName); err != nil {
  238. // c_log.GlobalLogger.Error("数据库查询报错:", err)
  239. // c.JSON(http.StatusBadRequest, commonEntity.Response{
  240. // Code: 500,
  241. // Msg: "数据库查询报错。",
  242. // })
  243. // return
  244. // }
  245. // c_log.GlobalLogger.Info("数据库查询成功:", result)
  246. // if !result[0].EndTime.Equal(defaultTime) {
  247. // c_log.GlobalLogger.Error("赛队", param.TeamName, "重复请求考试结束接口!")
  248. // c.JSON(http.StatusBadRequest, commonEntity.Response{
  249. // Code: 500,
  250. // Msg: "重复请求。",
  251. // })
  252. // return
  253. // }
  254. // // 更新到数据库
  255. // sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["exam-update-end_time-by-team_name.sql"])
  256. // if err := c_db.DoTx(sqlTemplate, []any{
  257. // time.Now(),
  258. // param.TeamName,
  259. // }); err != nil {
  260. // c_log.GlobalLogger.Error("插入数据报错:", err)
  261. // c.JSON(http.StatusBadRequest, commonEntity.Response{
  262. // Code: 500,
  263. // Msg: "插入数据报错。",
  264. // })
  265. // return
  266. // }
  267. // c.JSON(http.StatusOK, commonEntity.Response{
  268. // Code: 200,
  269. // Msg: "插入数据成功。",
  270. // })
  271. //}
  272. // 分页查询
  273. // 如果日期为默认值,则返回空""
  274. func Page(c *gin.Context) {
  275. param := new(webServerEntity.ExamPagePao)
  276. _ = c.ShouldBindJSON(&param)
  277. var resultPos []webServerEntity.ExamPo
  278. var resultPosTotal []int
  279. var pageSql string
  280. var totalSql string
  281. offset := (param.CurrentPage - 1) * param.PageSize
  282. size := param.PageSize
  283. if param.TeamName == "" && param.Topic == "" {
  284. pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page.sql"])
  285. totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total.sql"])
  286. err := c_db.MysqlDb.Select(&resultPos, pageSql, offset, size)
  287. if err != nil {
  288. c_log.GlobalLogger.Error(err)
  289. }
  290. err = c_db.MysqlDb.Select(&resultPosTotal, totalSql)
  291. if err != nil {
  292. c_log.GlobalLogger.Error(err)
  293. }
  294. }
  295. if param.TeamName != "" && param.Topic == "" {
  296. pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-team_name.sql"])
  297. totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-team_name.sql"])
  298. _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.TeamName+"%", offset, size)
  299. _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.TeamName+"%")
  300. }
  301. if param.TeamName == "" && param.Topic != "" {
  302. pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-topic.sql"])
  303. totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-topic.sql"])
  304. _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.Topic+"%", offset, size)
  305. _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.Topic+"%")
  306. }
  307. if param.TeamName != "" && param.Topic != "" {
  308. pageSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-page-by-team_name-and-topic.sql"])
  309. totalSql, _ = util.ReadFile(c_db.SqlFilesMap["exam-select-total-by-team_name-and-topic.sql"])
  310. _ = c_db.MysqlDb.Select(&resultPos, pageSql, "%"+param.TeamName+"%", "%"+param.Topic+"%", offset, size)
  311. _ = c_db.MysqlDb.Select(&resultPosTotal, totalSql, "%"+param.TeamName+"%", "%"+param.Topic+"%")
  312. }
  313. var resultVos []webServerEntity.ExamVo
  314. for _, po := range resultPos {
  315. resultVos = append(resultVos, webServerEntity.ExamVo{
  316. Id: po.Id,
  317. TeamName: po.TeamName,
  318. Topic: po.Topic,
  319. BeginTime: util.GetTimeString(po.BeginTime),
  320. EndTime: util.GetTimeString(po.EndTime),
  321. ScoreOnline: po.ScoreOnline,
  322. ScoreOffline: po.ScoreOffline,
  323. ScoreFinal: po.ScoreFinal,
  324. Details: po.Details,
  325. ScoreReportPath: po.ScoreReportPath,
  326. })
  327. }
  328. c.JSON(http.StatusOK, commonEntity.Response{
  329. Code: 200,
  330. Msg: "分页查询成功!",
  331. Data: resultVos,
  332. Total: resultPosTotal[0],
  333. })
  334. }
  335. // 评分报告pdf下载
  336. func Report(c *gin.Context) {
  337. param := new(webServerEntity.ExamReportPao)
  338. // 映射到结构体
  339. if err := c.ShouldBindJSON(&param); err != nil {
  340. c_log.GlobalLogger.Error("接收请求参数报错:", err)
  341. c.JSON(http.StatusBadRequest, commonEntity.Response{
  342. Code: 500,
  343. Msg: "请求体解析失败。",
  344. })
  345. return
  346. }
  347. // 查询打分详情
  348. var detailsResult []string
  349. selectSql, err := util.ReadFile(c_db.SqlFilesMap["exam-select-details-by-id.sql"])
  350. if err != nil {
  351. c_log.GlobalLogger.Error("读取sql文件报错:", err)
  352. c.JSON(http.StatusBadRequest, commonEntity.Response{
  353. Code: 500,
  354. Msg: "读取sql文件报错。",
  355. })
  356. return
  357. }
  358. // 可以传参数
  359. if err = c_db.MysqlDb.Select(&detailsResult, selectSql, param.Id); err != nil {
  360. c_log.GlobalLogger.Error("数据库查询报错:", err)
  361. c.JSON(http.StatusBadRequest, commonEntity.Response{
  362. Code: 500,
  363. Msg: "数据库查询报错。",
  364. })
  365. return
  366. }
  367. details := detailsResult[0]
  368. // 解析详情为数组
  369. var detailsDtos []webServerEntity.DetailsDto
  370. _ = json.Unmarshal([]byte(details), &detailsDtos)
  371. layout := "2006-01-02 15:04:05"
  372. beginTime, _ := time.Parse(layout, param.BeginTime)
  373. endTime, _ := time.Parse(layout, param.EndTime)
  374. // 1 根据ID查询数据
  375. // 2 根据数据生成pdf
  376. // 3 创建pdf文件
  377. {
  378. // 初始化 pdf 对象
  379. pdf := gopdf.GoPdf{}
  380. pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})
  381. // 添加字体文件到pdf
  382. err := pdf.AddTTFFont("simfang", infra.ApplicationYaml.Pdf.Ttf)
  383. if err != nil {
  384. log.Print(err.Error())
  385. return
  386. }
  387. leftMargin := 70.0
  388. topMargin := 100.0
  389. lineHeight := 25.0
  390. tableWidth := 450.0
  391. pdf.SetLeftMargin(leftMargin) // 设置左边距
  392. alignOptionText := gopdf.CellOption{Align: gopdf.Left | gopdf.Middle}
  393. //alignOptionText2 := gopdf.CellOption{Align: gopdf.Center | gopdf.Middle}
  394. alignOptionTable := gopdf.CellOption{Align: gopdf.Center | gopdf.Middle, Border: gopdf.Left | gopdf.Right | gopdf.Bottom | gopdf.Top}
  395. alignOptionTable2 := gopdf.CellOption{Align: gopdf.Left | gopdf.Middle}
  396. // ------- 封面页 -------
  397. pdf.AddPage()
  398. {
  399. err = pdf.SetFont("simfang", "", 36)
  400. err = pdf.Image(infra.ApplicationYaml.Pdf.BackgroundPng, 0, 0, nil) // 背景图片
  401. err = pdf.Image(infra.ApplicationYaml.Pdf.LogoPng, 20, 20, &gopdf.Rect{W: 320, H: 100})
  402. {
  403. pdf.SetXY(100, 250)
  404. _ = pdf.Text("车路云一体化算法挑战赛")
  405. }
  406. {
  407. pdf.SetXY(220, 315) // 200
  408. _ = pdf.Text("评分报告")
  409. }
  410. _ = pdf.SetFont("simfang", "", 14)
  411. currentLine := 0.0
  412. tableCellWidth := 100.0
  413. tableLeftMartin := 190.0
  414. tableTopMartin := 500.0
  415. {
  416. {
  417. pdf.SetXY(tableLeftMartin, tableTopMartin+currentLine*lineHeight)
  418. _ = pdf.CellWithOption(&gopdf.Rect{W: 100, H: lineHeight}, "赛队编号:", alignOptionTable2)
  419. }
  420. {
  421. pdf.SetXY(tableLeftMartin+1.0*tableCellWidth, tableTopMartin+currentLine*lineHeight)
  422. _ = pdf.CellWithOption(&gopdf.Rect{W: 100, H: lineHeight}, param.TeamName, alignOptionTable2)
  423. }
  424. currentLine++
  425. }
  426. {
  427. {
  428. pdf.SetXY(tableLeftMartin, tableTopMartin+currentLine*lineHeight)
  429. _ = pdf.CellWithOption(&gopdf.Rect{W: 100, H: lineHeight}, "评测车型:", alignOptionTable2)
  430. }
  431. {
  432. pdf.SetXY(tableLeftMartin+1.0*tableCellWidth, tableTopMartin+currentLine*lineHeight)
  433. _ = pdf.CellWithOption(&gopdf.Rect{W: 100, H: lineHeight}, "多功能车平台", alignOptionTable2)
  434. }
  435. currentLine++
  436. }
  437. {
  438. {
  439. pdf.SetXY(tableLeftMartin, tableTopMartin+currentLine*lineHeight)
  440. _ = pdf.CellWithOption(&gopdf.Rect{W: 100, H: lineHeight}, "评测地点:", alignOptionTable2)
  441. }
  442. {
  443. pdf.SetXY(tableLeftMartin+1.0*tableCellWidth, tableTopMartin+currentLine*lineHeight)
  444. _ = pdf.CellWithOption(&gopdf.Rect{W: 100, H: lineHeight}, "天津生态城", alignOptionTable2)
  445. }
  446. currentLine++
  447. }
  448. {
  449. {
  450. pdf.SetXY(tableLeftMartin, tableTopMartin+currentLine*lineHeight)
  451. _ = pdf.CellWithOption(&gopdf.Rect{W: 100, H: lineHeight}, "报告时间:", alignOptionTable2)
  452. }
  453. {
  454. pdf.SetXY(tableLeftMartin+1.0*tableCellWidth, tableTopMartin+currentLine*lineHeight)
  455. _ = pdf.CellWithOption(&gopdf.Rect{W: 100, H: lineHeight}, time.Now().Format("2006年01月02日"), alignOptionTable2)
  456. }
  457. currentLine++
  458. }
  459. {
  460. pdf.SetXY(165, 775)
  461. _ = pdf.Cell(nil, "国汽(北京)智能网联汽车研究院有限公司")
  462. }
  463. }
  464. // todo 先不要目录页
  465. //// ------- 目录页 -------
  466. //{
  467. // pdf.AddPage()
  468. // currentLine := 0.0
  469. // {
  470. // err = pdf.SetFont("simfang", "", 18)
  471. // pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  472. // _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "目录", alignOptionText2)
  473. // currentLine++
  474. // currentLine++
  475. // }
  476. // {
  477. // err = pdf.SetFont("simfang", "", 14)
  478. // pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  479. // currentLine++
  480. // _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "1. 总体情况 ............................... 1", alignOptionText)
  481. // }
  482. //}
  483. // ------- 详情页 第一页 -------
  484. {
  485. pdf.AddPage()
  486. currentLine := 0.0
  487. // 1. 总体分数情况
  488. {
  489. {
  490. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  491. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "1. 总体分数情况", alignOptionText)
  492. currentLine++
  493. }
  494. {
  495. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  496. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "整体得分:"+util.ToString(param.ScoreFinal), alignOptionText)
  497. currentLine++
  498. }
  499. {
  500. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  501. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "线上得分:"+util.ToString(param.ScoreFinal), alignOptionText)
  502. currentLine++
  503. }
  504. {
  505. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  506. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "线下得分:", alignOptionText)
  507. currentLine++
  508. }
  509. {
  510. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  511. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "整体耗时:"+endTime.Sub(beginTime).String(), alignOptionText)
  512. currentLine++
  513. }
  514. }
  515. // 2. 分数详情
  516. {
  517. //columnNumber := 4.0 // 列数
  518. //tableCellWidth := tableWidth / columnNumber // 单元格宽度
  519. tableCellWidth1 := 50.0
  520. tableCellWidth2 := 300.0
  521. tableCellWidth3 := 70.0
  522. {
  523. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  524. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "2. 分数详情", alignOptionText)
  525. currentLine++
  526. }
  527. // 分数详情表格
  528. {
  529. {
  530. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  531. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth1, H: lineHeight}, "序号", alignOptionTable)
  532. }
  533. {
  534. pdf.SetXY(leftMargin+tableCellWidth1, topMargin+currentLine*lineHeight)
  535. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth2, H: lineHeight}, "场景名称", alignOptionTable)
  536. }
  537. {
  538. pdf.SetXY(leftMargin+tableCellWidth1+tableCellWidth2, topMargin+currentLine*lineHeight)
  539. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth3, H: lineHeight}, "线上得分", alignOptionTable)
  540. }
  541. currentLine++
  542. }
  543. for i := 0; i < len(detailsDtos); i++ {
  544. {
  545. {
  546. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  547. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth1, H: lineHeight}, util.ToString(i+1), alignOptionTable)
  548. }
  549. {
  550. pdf.SetXY(leftMargin+tableCellWidth1, topMargin+currentLine*lineHeight)
  551. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth2, H: lineHeight}, detailsDtos[i].SceneName, alignOptionTable)
  552. }
  553. {
  554. pdf.SetXY(leftMargin+tableCellWidth1+tableCellWidth2, topMargin+currentLine*lineHeight)
  555. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth3, H: lineHeight}, util.ToString(detailsDtos[i].TotalScore+detailsDtos[i].DeductedScore), alignOptionTable)
  556. }
  557. currentLine++
  558. }
  559. }
  560. }
  561. }
  562. // ------- 详情页 第二页 -------
  563. var detailsDtos2 []webServerEntity.DetailsDto // 扣分的场景
  564. for _, detailsDto := range detailsDtos {
  565. if detailsDto.Reason != "" {
  566. detailsDtos2 = append(detailsDtos2, detailsDto)
  567. }
  568. }
  569. size := len(detailsDtos2)
  570. {
  571. pdf.AddPage()
  572. currentLine := 0.0
  573. // 3. 线上扣分详情
  574. {
  575. //columnNumber := 3.0 // 列数
  576. //tableCellWidth := tableWidth / columnNumber // 单元格宽度
  577. tableCellWidth := 450.0 // 单元格宽度
  578. {
  579. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  580. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "3. 线上扣分详情", alignOptionText)
  581. currentLine++
  582. }
  583. for i := 0; i < 8; i++ {
  584. // 场景1
  585. {
  586. {
  587. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  588. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "("+util.ToString(i+1)+")"+detailsDtos2[i].SceneName, alignOptionText)
  589. currentLine++
  590. }
  591. // 场景1 表格
  592. {
  593. //{
  594. // pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  595. // _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "序号", alignOptionTable)
  596. //}
  597. //{
  598. // pdf.SetXY(leftMargin+1.0*tableCellWidth, topMargin+currentLine*lineHeight)
  599. // _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "扣分点", alignOptionTable)
  600. //}
  601. {
  602. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  603. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "扣分说明", alignOptionTable)
  604. }
  605. currentLine++
  606. }
  607. //for j := 0; j < 1; j++ {
  608. {
  609. //{
  610. // pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  611. // _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, util.ToString(j+1), alignOptionTable)
  612. //}
  613. //{
  614. // pdf.SetXY(leftMargin+1.0*tableCellWidth, topMargin+currentLine*lineHeight)
  615. // _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "xxx", alignOptionTable)
  616. //}
  617. {
  618. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  619. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, detailsDtos2[i].Reason, alignOptionTable)
  620. }
  621. currentLine++
  622. }
  623. //}
  624. }
  625. }
  626. }
  627. //if size <= 8 {
  628. // // 4 作品优化建议
  629. // {
  630. // pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  631. // _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "4. 作品优化建议", alignOptionText)
  632. // currentLine++
  633. // }
  634. //}
  635. }
  636. // ------- 详情页 第三页 -------
  637. if size > 8 {
  638. pdf.AddPage()
  639. currentLine := 0.0
  640. // 3. 线上扣分详情
  641. {
  642. columnNumber := 3.0 // 列数
  643. tableCellWidth := tableWidth / columnNumber // 单元格宽度
  644. for i := 5; i < 10; i++ {
  645. // 场景1
  646. {
  647. {
  648. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  649. _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "("+util.ToString(i+1)+")xxxx场景", alignOptionText)
  650. currentLine++
  651. }
  652. // 场景1 表格
  653. {
  654. {
  655. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  656. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "序号", alignOptionTable)
  657. }
  658. {
  659. pdf.SetXY(leftMargin+1.0*tableCellWidth, topMargin+currentLine*lineHeight)
  660. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "扣分点", alignOptionTable)
  661. }
  662. {
  663. pdf.SetXY(leftMargin+2.0*tableCellWidth, topMargin+currentLine*lineHeight)
  664. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "扣分说明", alignOptionTable)
  665. }
  666. currentLine++
  667. }
  668. for j := 0; j < 2; j++ {
  669. {
  670. {
  671. pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  672. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, util.ToString(j+1), alignOptionTable)
  673. }
  674. {
  675. pdf.SetXY(leftMargin+1.0*tableCellWidth, topMargin+currentLine*lineHeight)
  676. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "xxx", alignOptionTable)
  677. }
  678. {
  679. pdf.SetXY(leftMargin+2.0*tableCellWidth, topMargin+currentLine*lineHeight)
  680. _ = pdf.CellWithOption(&gopdf.Rect{W: tableCellWidth, H: lineHeight}, "xxx", alignOptionTable)
  681. }
  682. currentLine++
  683. }
  684. }
  685. }
  686. }
  687. }
  688. //// 4 作品优化建议
  689. //{
  690. // pdf.SetXY(leftMargin, topMargin+currentLine*lineHeight)
  691. // _ = pdf.CellWithOption(&gopdf.Rect{W: tableWidth, H: lineHeight}, "4. 作品优化建议", alignOptionText)
  692. // currentLine++
  693. //}
  694. }
  695. //// ------- 附录页 -------
  696. //pdf.AddPage()
  697. //{
  698. // pdf.SetXY(leftMargin, topMargin)
  699. // _ = pdf.Text("附件:评分规则")
  700. // pdf.AddExternalLink("https://www.baidu.com/", leftMargin, topMargin-lineHeight, 6*14, lineHeight)
  701. //}
  702. // 写入本地
  703. _ = pdf.WritePdf("D:\\hello.pdf")
  704. }
  705. // 打开要发送的文件
  706. file, err := os.Open("D:\\hello.pdf")
  707. if err != nil {
  708. c.String(http.StatusNotFound, "文件未找到")
  709. return
  710. }
  711. defer file.Close()
  712. // 将文件复制到响应主体
  713. _, err = io.Copy(c.Writer, file)
  714. if err != nil {
  715. c.String(http.StatusInternalServerError, "无法复制文件到响应体")
  716. return
  717. }
  718. }