h_exam.go 27 KB

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