电子考评.go 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824
  1. package controllers
  2. import (
  3. // 根据实际项目结构调整导入路径
  4. "Tech/common"
  5. "Tech/models"
  6. "bytes"
  7. "encoding/json"
  8. "fmt"
  9. "io/ioutil"
  10. "mime/multipart"
  11. "net/http"
  12. "os"
  13. "os/exec"
  14. "time"
  15. "github.com/beego/beego/v2/client/orm"
  16. beego "github.com/beego/beego/v2/server/web"
  17. // 新增导入
  18. )
  19. type EvaLogController struct {
  20. beego.Controller
  21. }
  22. // /eva/record/list 获取课程评价记录列表
  23. func (c *EvaLogController) EvaRecordList() {
  24. type Request struct {
  25. RunLogID int `json:"runLogID"`
  26. }
  27. var req Request
  28. requestBody := c.Ctx.Input.RequestBody
  29. println("Request Body:", string(requestBody))
  30. if len(requestBody) > 0 {
  31. if err := json.Unmarshal(requestBody, &req); err != nil {
  32. c.Ctx.Output.SetStatus(400)
  33. c.Data["json"] = map[string]interface{}{
  34. "code": 400,
  35. "msg": "请求参数格式错误: " + err.Error(),
  36. }
  37. c.ServeJSON()
  38. return
  39. }
  40. }
  41. o := orm.NewOrm()
  42. // 查询所有角色信息用于关联角色名称
  43. var roleInfos []*models.RoleInfo
  44. _, err := o.QueryTable(new(models.RoleInfo)).All(&roleInfos)
  45. if err != nil {
  46. c.Data["json"] = map[string]interface{}{
  47. "code": 500,
  48. "msg": "查询角色信息失败: " + err.Error(),
  49. }
  50. c.ServeJSON()
  51. return
  52. }
  53. // 创建角色ID到角色名称的映射
  54. roleMap := make(map[int]string)
  55. for _, role := range roleInfos {
  56. roleMap[role.RoleID] = role.RoleName
  57. }
  58. // 定义联合查询的结果结构体
  59. type EvaRecord struct {
  60. // CourseEvaLog 字段
  61. RunLogID int `orm:"column(运行记录编号)" json:"runLogID"`
  62. RunCourseID int `orm:"column(运行课程ID)" json:"runCourseID"`
  63. RunOperID int `orm:"column(运行操作ID)" json:"runOperID"`
  64. OperTime int `orm:"column(操作时间)" json:"operTime"`
  65. OperScore float64 `orm:"column(操作得分)" json:"operScore"`
  66. SeqNo int `orm:"column(序号)" json:"seqNo"`
  67. EvaTargetRole int `orm:"column(评价目标角色)" json:"evaTargetRole"`
  68. EvaName string `orm:"column(评价名称)" json:"evaName"`
  69. EvaMapID int `orm:"column(评价映射ID)" json:"evaMapID"`
  70. EvaType int `orm:"column(评价类型)" json:"evaType"`
  71. EvaSource int `orm:"column(评价来源)" json:"evaSource"`
  72. DisqualificationItem int `orm:"column(失格项)" json:"disqualificationItem"`
  73. IsOperated int `orm:"column(是否操作)" json:"isOperated"` // 节点是否操作 0 未操作 1 已操作
  74. EvaScore int `orm:"column(评价赋分)" json:"evaScore"`
  75. EvaDeduction int `orm:"column(评价失分)" json:"evaDeduction"`
  76. }
  77. // 定义带有角色名称的扩展结果结构体
  78. type EvaRecordWithRoleName struct {
  79. // CourseEvaLog 字段
  80. RunLogID int `json:"runLogID"`
  81. RunCourseID int `json:"runCourseID"`
  82. RunOperID int `json:"runOperID"`
  83. OperTime int `json:"operTime"`
  84. OperScore float64 `json:"operScore"`
  85. SeqNo int `json:"seqNo"`
  86. EvaTargetRole int `json:"evaTargetRole"`
  87. EvaName string `json:"evaName"`
  88. EvaMapID int `json:"evaMapID"`
  89. EvaType int `json:"evaType"`
  90. EvaSource int `json:"evaSource"`
  91. DisqualificationItem int `json:"disqualificationItem"`
  92. IsOperated int `json:"isOperated"` // 节点是否操作 0 未操作 1 已操作
  93. EvaScore int `json:"evaScore"`
  94. EvaDeduction int `json:"evaDeduction"`
  95. //课程名称
  96. CourseName string `json:"courseName"`
  97. TargetRoleName string `json:"targetRoleName"`
  98. }
  99. var evaRecords []EvaRecord
  100. var count int64
  101. // 如果RunLogID > 0,则查询对应的课程运行评价列表
  102. // 否则查询最新的课程运行评价列表
  103. var runLogID int
  104. if req.RunLogID > 0 {
  105. runLogID = req.RunLogID
  106. // 根据运行记录编号查询联合评价记录列表
  107. count, err = o.Raw(`
  108. SELECT
  109. l.运行记录编号,
  110. l.运行课程ID,
  111. l.运行操作ID,
  112. l.操作时间,
  113. l.操作得分,
  114. l.序号,
  115. l.评价目标角色,
  116. l.评价名称,
  117. l.评价映射ID,
  118. l.评价类型,
  119. l.评价来源,
  120. l.失格项,
  121. l.是否操作,
  122. l.评价赋分,
  123. l.评价失分
  124. FROM CourseEvaLog l
  125. WHERE l.运行记录编号 = ?
  126. `, req.RunLogID).QueryRows(&evaRecords)
  127. } else {
  128. // 查询最新的课程运行记录ID
  129. var latestRunLog models.CourseRunLog
  130. err = o.QueryTable(new(models.CourseRunLog)).
  131. OrderBy("-RunLogID").
  132. One(&latestRunLog)
  133. if err != nil {
  134. if err == orm.ErrNoRows {
  135. // 没有运行记录
  136. c.Data["json"] = map[string]interface{}{
  137. "code": 404,
  138. "msg": "未找到任何课程运行记录",
  139. }
  140. c.ServeJSON()
  141. return
  142. } else {
  143. // 查询出错
  144. c.Data["json"] = map[string]interface{}{
  145. "code": 500,
  146. "msg": "查询课程运行记录失败: " + err.Error(),
  147. }
  148. c.ServeJSON()
  149. return
  150. }
  151. }
  152. runLogID = latestRunLog.RunLogID
  153. // 根据最新运行记录ID查询联合评价记录列表
  154. count, err = o.Raw(`
  155. SELECT
  156. l.运行记录编号,
  157. l.运行课程ID,
  158. l.运行操作ID,
  159. l.操作时间,
  160. l.操作得分,
  161. l.序号,
  162. l.评价目标角色,
  163. l.评价名称,
  164. l.评价映射ID,
  165. l.评价类型,
  166. l.评价来源,
  167. l.失格项,
  168. l.是否操作,
  169. l.评价赋分,
  170. l.评价失分
  171. FROM CourseEvaLog l
  172. WHERE l.运行记录编号 = ?
  173. `, latestRunLog.RunLogID).QueryRows(&evaRecords)
  174. }
  175. if err != nil {
  176. c.Data["json"] = map[string]interface{}{
  177. "code": 500,
  178. "msg": "查询评价记录列表失败: " + err.Error(),
  179. }
  180. c.ServeJSON()
  181. return
  182. }
  183. // 查询课程运行记录以获取课程名称
  184. var courseRunLog models.CourseRunLog
  185. err = o.QueryTable(new(models.CourseRunLog)).Filter("RunLogID", runLogID).One(&courseRunLog)
  186. if err != nil && err != orm.ErrNoRows {
  187. c.Data["json"] = map[string]interface{}{
  188. "code": 500,
  189. "msg": "查询课程运行记录失败: " + err.Error(),
  190. }
  191. c.ServeJSON()
  192. return
  193. }
  194. // 为每个评价项添加角色名称和课程名称(适配 evaRecords 为值切片)
  195. evaRecordsWithRoles := make([]*EvaRecordWithRoleName, len(evaRecords))
  196. for i, record := range evaRecords {
  197. evaRecordsWithRoles[i] = &EvaRecordWithRoleName{
  198. RunLogID: record.RunLogID,
  199. RunCourseID: record.RunCourseID,
  200. RunOperID: record.RunOperID,
  201. OperTime: record.OperTime,
  202. OperScore: record.OperScore,
  203. SeqNo: record.SeqNo,
  204. EvaTargetRole: record.EvaTargetRole,
  205. EvaName: record.EvaName,
  206. EvaMapID: record.EvaMapID,
  207. EvaType: record.EvaType,
  208. EvaSource: record.EvaSource,
  209. DisqualificationItem: record.DisqualificationItem,
  210. EvaScore: record.EvaScore,
  211. EvaDeduction: record.EvaDeduction,
  212. CourseName: courseRunLog.RunCourseName,
  213. IsOperated: record.IsOperated,
  214. TargetRoleName: roleMap[record.EvaTargetRole],
  215. }
  216. }
  217. c.Data["json"] = map[string]interface{}{
  218. "code": 200,
  219. "count": count,
  220. "msg": "成功",
  221. "data": evaRecordsWithRoles,
  222. }
  223. c.ServeJSON()
  224. }
  225. // /eva/setoperated 设置节点是否操作,并计算得分
  226. func (c *EvaLogController) Setoperated() {
  227. type Request struct {
  228. RunLogID int `json:"runLogID"`
  229. SeqNo int `json:"seqNo"`
  230. IsOperated int `json:"isOperated"` // 0:未操作 1:已操作
  231. }
  232. var req Request
  233. requestBody := c.Ctx.Input.RequestBody
  234. println("Request Body:", string(requestBody))
  235. if err := json.Unmarshal(requestBody, &req); err != nil {
  236. c.Ctx.Output.SetStatus(400)
  237. c.Data["json"] = map[string]interface{}{
  238. "code": 400,
  239. "msg": "请求参数格式错误: " + err.Error(),
  240. }
  241. c.ServeJSON()
  242. return
  243. }
  244. if req.RunLogID == 0 || req.SeqNo == 0 {
  245. c.Ctx.Output.SetStatus(400)
  246. c.Data["json"] = map[string]interface{}{
  247. "code": 400,
  248. "msg": "缺少必要参数",
  249. }
  250. c.ServeJSON()
  251. return
  252. }
  253. o := orm.NewOrm()
  254. // 查询对应的评价标准信息,直接从CourseEvaLog获取评分信息
  255. var evaLog models.CourseEvaLog
  256. err := o.Raw(`
  257. SELECT *
  258. FROM CourseEvaLog
  259. WHERE 运行记录编号 = ? AND 序号 = ?
  260. `, req.RunLogID, req.SeqNo).QueryRow(&evaLog)
  261. if err != nil {
  262. c.Data["json"] = map[string]interface{}{
  263. "code": 500,
  264. "msg": "查询评价标准信息失败: " + err.Error(),
  265. }
  266. c.ServeJSON()
  267. return
  268. }
  269. // 查询课程运行记录信息
  270. var courseRunLog models.CourseRunLog
  271. err = o.Raw(`
  272. SELECT *
  273. FROM CourseRunLog
  274. WHERE 运行记录编号 = ?
  275. `, req.RunLogID).QueryRow(&courseRunLog)
  276. if err != nil {
  277. c.Data["json"] = map[string]interface{}{
  278. "code": 500,
  279. "msg": "查询课程运行记录失败: " + err.Error(),
  280. }
  281. c.ServeJSON()
  282. return
  283. }
  284. // 计算操作时间
  285. operTime := 0
  286. // 只有在课程未结束时才计算操作时间(暂停标记为-1表示课程结束)
  287. if courseRunLog.PauseFlag != -1 {
  288. // 计算课程用时(从开始时间到当前时间的秒数)
  289. startTime, err := time.ParseInLocation("2006-01-02 15:04:05", courseRunLog.RunDateTime, time.Local)
  290. if err != nil {
  291. c.Data["json"] = map[string]interface{}{
  292. "code": 500,
  293. "msg": "解析课程开始时间失败: " + err.Error(),
  294. "runStatus": 0,
  295. "data": nil,
  296. }
  297. c.ServeJSON()
  298. return
  299. }
  300. operTime = int(time.Since(startTime).Seconds()) - courseRunLog.PauseTotalTime
  301. // 确保操作时间不为负数
  302. if operTime < 0 {
  303. operTime = 0
  304. }
  305. }
  306. // 计算操作得分
  307. operScore := 0.0
  308. if req.IsOperated == 1 {
  309. // 如果已操作,得分为评价赋分-评价失分
  310. operScore = float64(evaLog.EvaScore - evaLog.EvaDeduction)
  311. }
  312. // 如果未操作,得分为0(默认值)
  313. // 更新CourseEvaLog表中的是否操作字段、操作得分和操作时间
  314. res, err := o.Raw(`
  315. UPDATE CourseEvaLog
  316. SET 是否操作 = ?, 操作得分 = ?, 操作时间 = ?
  317. WHERE 运行记录编号 = ? AND 序号 = ?
  318. `, req.IsOperated, operScore, operTime, req.RunLogID, req.SeqNo).Exec()
  319. if err != nil {
  320. c.Data["json"] = map[string]interface{}{
  321. "code": 500,
  322. "msg": "更新节点操作状态失败: " + err.Error(),
  323. }
  324. c.ServeJSON()
  325. return
  326. }
  327. fmt.Println(res)
  328. // 重新计算整个课程的总得分
  329. // 查询该课程运行记录下的所有评价项
  330. var totalScore float64
  331. err = o.Raw(`
  332. SELECT COALESCE(SUM(操作得分), 0)
  333. FROM CourseEvaLog
  334. WHERE 运行记录编号 = ?
  335. `, req.RunLogID).QueryRow(&totalScore)
  336. if err != nil {
  337. c.Data["json"] = map[string]interface{}{
  338. "code": 500,
  339. "msg": "计算总得分失败: " + err.Error(),
  340. }
  341. c.ServeJSON()
  342. return
  343. }
  344. // 更新课程运行日志中的总得分
  345. _, err = o.Raw(`
  346. UPDATE CourseRunLog
  347. SET 课程得分 = ?
  348. WHERE 运行记录编号 = ?
  349. `, totalScore, req.RunLogID).Exec()
  350. if err != nil {
  351. c.Data["json"] = map[string]interface{}{
  352. "code": 500,
  353. "msg": "更新总得分失败: " + err.Error(),
  354. }
  355. c.ServeJSON()
  356. return
  357. }
  358. c.Data["json"] = map[string]interface{}{
  359. "code": 200,
  360. "msg": "设置节点操作状态成功",
  361. "data": map[string]interface{}{
  362. "runLogID": req.RunLogID,
  363. "seqNo": req.SeqNo,
  364. "isOperated": req.IsOperated,
  365. "operScore": operScore,
  366. "operTime": operTime,
  367. "totalScore": totalScore,
  368. },
  369. }
  370. c.ServeJSON()
  371. }
  372. // /evaLog/add 添加评价日志记录
  373. func (c *EvaLogController) Add() {
  374. var eva models.CourseEvaLog
  375. requestBody := c.Ctx.Input.RequestBody
  376. println("Request Body:", string(requestBody))
  377. if err := json.Unmarshal(requestBody, &eva); err != nil {
  378. c.Ctx.Output.SetStatus(400)
  379. c.Data["json"] = map[string]interface{}{
  380. "code": 400,
  381. "msg": "请求参数格式错误: " + err.Error(),
  382. }
  383. c.ServeJSON()
  384. return
  385. }
  386. if eva.RunLogID == 0 {
  387. c.Ctx.Output.SetStatus(400)
  388. c.Data["json"] = map[string]interface{}{
  389. "code": 400,
  390. "msg": "缺少运行记录ID参数",
  391. }
  392. c.ServeJSON()
  393. return
  394. }
  395. o := orm.NewOrm()
  396. // 如果SeqNo为0,则查询该运行记录下最大的SeqNo并加1
  397. if eva.SeqNo == 0 {
  398. var maxSeqNo int
  399. err := o.Raw("SELECT COALESCE(MAX(序号), 0) FROM CourseEvaLog WHERE 运行记录编号 = ?", eva.RunLogID).QueryRow(&maxSeqNo)
  400. if err != nil {
  401. c.Data["json"] = map[string]interface{}{
  402. "code": 500,
  403. "msg": "查询序号失败: " + err.Error(),
  404. }
  405. c.ServeJSON()
  406. return
  407. }
  408. eva.SeqNo = maxSeqNo + 1
  409. } else {
  410. // 如果指定了SeqNo,检查该SeqNo是否已存在
  411. var count int64
  412. count, err := o.QueryTable(new(models.CourseEvaLog)).
  413. Filter("RunLogID", eva.RunLogID).
  414. Filter("SeqNo", eva.SeqNo).
  415. Count()
  416. if err != nil {
  417. c.Data["json"] = map[string]interface{}{
  418. "code": 500,
  419. "msg": "查询序号失败: " + err.Error(),
  420. }
  421. c.ServeJSON()
  422. return
  423. }
  424. // 如果SeqNo已存在,则将该SeqNo及之后的所有序号都加1
  425. if count > 0 {
  426. // 使用倒序更新,避免主键冲突
  427. _, err := o.Raw("UPDATE CourseEvaLog SET 序号 = 序号 + 1 WHERE 运行记录编号 = ? AND 序号 >= ? ORDER BY 序号 DESC",
  428. eva.RunLogID, eva.SeqNo).Exec()
  429. if err != nil {
  430. c.Data["json"] = map[string]interface{}{
  431. "code": 500,
  432. "msg": "调整序号失败: " + err.Error(),
  433. }
  434. c.ServeJSON()
  435. return
  436. }
  437. }
  438. }
  439. // 设置评价来源为教员自定义
  440. eva.EvaSource = 1
  441. // 默认设置为未操作
  442. eva.IsOperated = 0
  443. // 默认操作得分为0
  444. eva.OperScore = 0
  445. _, err := o.Insert(&eva)
  446. if err != nil {
  447. c.Data["json"] = map[string]interface{}{
  448. "code": 500,
  449. "msg": "添加评价失败: " + err.Error(),
  450. }
  451. c.ServeJSON()
  452. return
  453. }
  454. c.Data["json"] = map[string]interface{}{
  455. "code": 200,
  456. "msg": "添加评价成功",
  457. "seqNo": eva.SeqNo,
  458. }
  459. c.ServeJSON()
  460. }
  461. // /evaLog/delete 删除评价日志记录
  462. func (c *EvaLogController) Delete() {
  463. type Request struct {
  464. RunLogID int `json:"runLogID"`
  465. SeqNo int `json:"seqNo"`
  466. }
  467. var req Request
  468. requestBody := c.Ctx.Input.RequestBody
  469. println("Request Body:", string(requestBody))
  470. if err := json.Unmarshal(requestBody, &req); err != nil {
  471. c.Ctx.Output.SetStatus(400)
  472. c.Data["json"] = map[string]interface{}{
  473. "code": 400,
  474. "msg": "请求参数格式错误: " + err.Error(),
  475. }
  476. c.ServeJSON()
  477. return
  478. }
  479. if req.RunLogID == 0 || req.SeqNo == 0 {
  480. c.Ctx.Output.SetStatus(400)
  481. c.Data["json"] = map[string]interface{}{
  482. "code": 400,
  483. "msg": "缺少必要参数",
  484. }
  485. c.ServeJSON()
  486. return
  487. }
  488. o := orm.NewOrm()
  489. // 先查询评价记录,判断是否来自流程图
  490. var eva models.CourseEvaLog
  491. err := o.Raw("SELECT * FROM CourseEvaLog WHERE 运行记录编号 = ? AND 序号 = ?", req.RunLogID, req.SeqNo).QueryRow(&eva)
  492. if err != nil {
  493. c.Data["json"] = map[string]interface{}{
  494. "code": 500,
  495. "msg": "查询评价记录失败: " + err.Error(),
  496. }
  497. c.ServeJSON()
  498. return
  499. }
  500. // 如果评价来自流程图(EvaSource=0),则不允许删除
  501. if eva.EvaSource == 0 {
  502. c.Data["json"] = map[string]interface{}{
  503. "code": 403,
  504. "msg": "来自流程图的评价不能删除",
  505. }
  506. c.ServeJSON()
  507. return
  508. }
  509. // 执行删除操作(仅对教员自定义的评价)
  510. result, err := o.Raw("DELETE FROM CourseEvaLog WHERE 运行记录编号 = ? AND 序号 = ?", req.RunLogID, req.SeqNo).Exec()
  511. if err != nil {
  512. c.Data["json"] = map[string]interface{}{
  513. "code": 500,
  514. "msg": "删除评价失败: " + err.Error(),
  515. }
  516. c.ServeJSON()
  517. return
  518. }
  519. n, _ := result.RowsAffected()
  520. if n == 0 {
  521. c.Data["json"] = map[string]interface{}{
  522. "code": 404,
  523. "msg": "未找到对应的评价记录",
  524. }
  525. c.ServeJSON()
  526. return
  527. }
  528. c.Data["json"] = map[string]interface{}{
  529. "code": 200,
  530. "msg": "删除评价成功",
  531. }
  532. c.ServeJSON()
  533. }
  534. // /evaLog/update 更新评价日志记录
  535. func (c *EvaLogController) Update() {
  536. var eva models.CourseEvaLog
  537. requestBody := c.Ctx.Input.RequestBody
  538. println("Request Body:", string(requestBody))
  539. if err := json.Unmarshal(requestBody, &eva); err != nil {
  540. c.Ctx.Output.SetStatus(400)
  541. c.Data["json"] = map[string]interface{}{
  542. "code": 400,
  543. "msg": "请求参数格式错误: " + err.Error(),
  544. }
  545. c.ServeJSON()
  546. return
  547. }
  548. if eva.RunLogID == 0 || eva.SeqNo == 0 {
  549. c.Ctx.Output.SetStatus(400)
  550. c.Data["json"] = map[string]interface{}{
  551. "code": 400,
  552. "msg": "缺少必要参数",
  553. }
  554. c.ServeJSON()
  555. return
  556. }
  557. o := orm.NewOrm()
  558. // 先尝试读取记录,判断是否存在
  559. existingEva := models.CourseEvaLog{RunLogID: eva.RunLogID, SeqNo: eva.SeqNo}
  560. err := o.Read(&existingEva, "RunLogID", "SeqNo")
  561. if err != nil {
  562. if err == orm.ErrNoRows {
  563. c.Data["json"] = map[string]interface{}{
  564. "code": 404,
  565. "msg": "未找到对应的评价记录",
  566. }
  567. c.ServeJSON()
  568. return
  569. } else {
  570. c.Data["json"] = map[string]interface{}{
  571. "code": 500,
  572. "msg": "查询评价记录失败: " + err.Error(),
  573. }
  574. c.ServeJSON()
  575. return
  576. }
  577. }
  578. // 如果请求中没有提供相应字段,则使用现有记录中的值
  579. if eva.EvaTargetRole == 0 {
  580. eva.EvaTargetRole = existingEva.EvaTargetRole
  581. }
  582. if eva.EvaName == "" {
  583. eva.EvaName = existingEva.EvaName
  584. }
  585. if eva.EvaMapID == 0 {
  586. eva.EvaMapID = existingEva.EvaMapID
  587. }
  588. // 评价来源始终使用现有记录中的值(保留原始来源)
  589. eva.EvaSource = existingEva.EvaSource
  590. // 修改为允许修改为失格项
  591. // if eva.DisqualificationItem == 0 {
  592. // eva.DisqualificationItem = existingEva.DisqualificationItem
  593. // }
  594. // 使用原生SQL更新记录
  595. _, err = o.Raw("UPDATE CourseEvaLog SET 评价目标角色=?, 评价名称=?, 评价映射ID=?, 评价类型=?, 评价赋分=?, 评价失分=?, 评价来源=?, 失格项=? WHERE 运行记录编号=? AND 序号=?",
  596. eva.EvaTargetRole, eva.EvaName, eva.EvaMapID, eva.EvaType, eva.EvaScore, eva.EvaDeduction, eva.EvaSource, eva.DisqualificationItem,
  597. eva.RunLogID, eva.SeqNo).Exec()
  598. if err != nil {
  599. c.Data["json"] = map[string]interface{}{
  600. "code": 500,
  601. "msg": "更新评价失败: " + err.Error(),
  602. }
  603. c.ServeJSON()
  604. return
  605. }
  606. c.Data["json"] = map[string]interface{}{
  607. "code": 200,
  608. "msg": "更新评价成功",
  609. "data": eva,
  610. }
  611. c.ServeJSON()
  612. }
  613. // /evalog/orderupdate 评价记录排序更新
  614. func (c *EvaLogController) Orderupdate() {
  615. type Request struct {
  616. RunLogID int `json:"runLogID"`
  617. SeqNo int `json:"seqNo"`
  618. SetOrder int `json:"setorder"`
  619. }
  620. var req Request
  621. requestBody := c.Ctx.Input.RequestBody
  622. println("Request Body:", string(requestBody))
  623. if err := json.Unmarshal(requestBody, &req); err != nil {
  624. c.Ctx.Output.SetStatus(400)
  625. c.Data["json"] = map[string]interface{}{
  626. "code": 400,
  627. "msg": "请求参数格式错误: " + err.Error(),
  628. }
  629. c.ServeJSON()
  630. return
  631. }
  632. if req.RunLogID == 0 || req.SeqNo == 0 || req.SetOrder == 0 {
  633. c.Ctx.Output.SetStatus(400)
  634. c.Data["json"] = map[string]interface{}{
  635. "code": 400,
  636. "msg": "缺少必要参数",
  637. }
  638. c.ServeJSON()
  639. return
  640. }
  641. o := orm.NewOrm()
  642. // 先尝试读取记录,判断是否存在
  643. existingEva := models.CourseEvaLog{RunLogID: req.RunLogID, SeqNo: req.SeqNo}
  644. err := o.Read(&existingEva, "RunLogID", "SeqNo")
  645. if err != nil {
  646. if err == orm.ErrNoRows {
  647. c.Data["json"] = map[string]interface{}{
  648. "code": 404,
  649. "msg": "未找到对应的评价记录",
  650. }
  651. } else {
  652. c.Data["json"] = map[string]interface{}{
  653. "code": 500,
  654. "msg": "查询评价记录失败: " + err.Error(),
  655. }
  656. }
  657. c.ServeJSON()
  658. return
  659. }
  660. // 获取该运行记录的所有评价记录,按序号排序
  661. var evaList []models.CourseEvaLog
  662. _, err = o.QueryTable(new(models.CourseEvaLog)).
  663. Filter("RunLogID", req.RunLogID).
  664. OrderBy("SeqNo").
  665. All(&evaList)
  666. if err != nil {
  667. c.Data["json"] = map[string]interface{}{
  668. "code": 500,
  669. "msg": "查询评价列表失败: " + err.Error(),
  670. }
  671. c.ServeJSON()
  672. return
  673. }
  674. // 检查边界条件
  675. if req.SetOrder == 1 && req.SeqNo <= 1 {
  676. // 上移但已经在最顶部
  677. c.Data["json"] = map[string]interface{}{
  678. "code": 400,
  679. "msg": "无法执行上移操作,已到达顶部",
  680. }
  681. c.ServeJSON()
  682. return
  683. }
  684. if req.SetOrder == 2 && req.SeqNo >= len(evaList) {
  685. // 下移但已经在最底部
  686. c.Data["json"] = map[string]interface{}{
  687. "code": 400,
  688. "msg": "无法执行下移操作,已到达底部",
  689. }
  690. c.ServeJSON()
  691. return
  692. }
  693. // 确定要交换的两个序号
  694. var targetSeqNo int
  695. if req.SetOrder == 1 {
  696. // 上移:与前一个序号交换
  697. targetSeqNo = req.SeqNo - 1
  698. } else {
  699. // 下移:与后一个序号交换
  700. targetSeqNo = req.SeqNo + 1
  701. }
  702. // 使用三步更新避免主键冲突
  703. // 第一步:将目标序号临时设为0
  704. _, err = o.Raw("UPDATE CourseEvaLog SET 序号 = 0 WHERE 运行记录编号 = ? AND 序号 = ?",
  705. req.RunLogID, targetSeqNo).Exec()
  706. if err != nil {
  707. c.Data["json"] = map[string]interface{}{
  708. "code": 500,
  709. "msg": "更新序号失败: " + err.Error(),
  710. }
  711. c.ServeJSON()
  712. return
  713. }
  714. // 第二步:将当前序号更新为目标序号
  715. _, err = o.Raw("UPDATE CourseEvaLog SET 序号 = ? WHERE 运行记录编号 = ? AND 序号 = ?",
  716. targetSeqNo, req.RunLogID, req.SeqNo).Exec()
  717. if err != nil {
  718. c.Data["json"] = map[string]interface{}{
  719. "code": 500,
  720. "msg": "更新序号失败: " + err.Error(),
  721. }
  722. c.ServeJSON()
  723. return
  724. }
  725. // 第三步:将临时序号0更新为当前序号
  726. _, err = o.Raw("UPDATE CourseEvaLog SET 序号 = ? WHERE 运行记录编号 = ? AND 序号 = 0",
  727. req.SeqNo, req.RunLogID).Exec()
  728. if err != nil {
  729. c.Data["json"] = map[string]interface{}{
  730. "code": 500,
  731. "msg": "更新序号失败: " + err.Error(),
  732. }
  733. c.ServeJSON()
  734. return
  735. }
  736. c.Data["json"] = map[string]interface{}{
  737. "code": 200,
  738. "msg": "顺序调整成功",
  739. }
  740. c.ServeJSON()
  741. }
  742. // 将秒数格式化为 HH:MM:SS 格式
  743. func formatSecondsToHMS(seconds int) string {
  744. hours := seconds / 3600
  745. minutes := (seconds % 3600) / 60
  746. secs := seconds % 60
  747. return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, secs)
  748. }
  749. // /evalog/export 导出课程评价记录
  750. func (c *EvaLogController) Export() {
  751. type Request struct {
  752. RunLogID int `json:"runLogID"`
  753. }
  754. var req Request
  755. requestBody := c.Ctx.Input.RequestBody
  756. println("Request Body:", string(requestBody))
  757. if err := json.Unmarshal(requestBody, &req); err != nil {
  758. c.Ctx.Output.SetStatus(400)
  759. c.Data["json"] = map[string]interface{}{
  760. "code": 400,
  761. "msg": "请求参数格式错误: " + err.Error(),
  762. }
  763. c.ServeJSON()
  764. return
  765. }
  766. if req.RunLogID == 0 {
  767. c.Ctx.Output.SetStatus(400)
  768. c.Data["json"] = map[string]interface{}{
  769. "code": 400,
  770. "msg": "缺少运行记录ID参数",
  771. }
  772. c.ServeJSON()
  773. return
  774. }
  775. o := orm.NewOrm()
  776. // 查询课程运行记录信息
  777. var courseRunLog models.CourseRunLog
  778. err := o.Raw(`SELECT * FROM CourseRunLog WHERE 运行记录编号 = ?`, req.RunLogID).QueryRow(&courseRunLog)
  779. if err != nil {
  780. if err == orm.ErrNoRows {
  781. c.Data["json"] = map[string]interface{}{
  782. "code": 404,
  783. "msg": "未找到对应的课程运行记录",
  784. }
  785. } else {
  786. c.Data["json"] = map[string]interface{}{
  787. "code": 500,
  788. "msg": "查询课程运行记录失败: " + err.Error(),
  789. }
  790. }
  791. c.ServeJSON()
  792. return
  793. }
  794. // 查询该课程运行记录下的所有评价项
  795. var evaLogs []models.CourseEvaLog
  796. _, err = o.Raw(`
  797. SELECT *
  798. FROM CourseEvaLog
  799. WHERE 运行记录编号 = ?
  800. ORDER BY 序号
  801. `, req.RunLogID).QueryRows(&evaLogs)
  802. if err != nil {
  803. c.Data["json"] = map[string]interface{}{
  804. "code": 500,
  805. "msg": "查询评价记录列表失败: " + err.Error(),
  806. }
  807. c.ServeJSON()
  808. return
  809. }
  810. // 查询所有角色信息用于关联角色名称
  811. var roleInfos []*models.RoleInfo
  812. _, err = o.QueryTable(new(models.RoleInfo)).All(&roleInfos)
  813. if err != nil {
  814. c.Data["json"] = map[string]interface{}{
  815. "code": 500,
  816. "msg": "查询角色信息失败: " + err.Error(),
  817. }
  818. c.ServeJSON()
  819. return
  820. }
  821. if courseRunLog.CourseTime == 0 {
  822. // 课程正在进行中,计算运行时间并返回详细信息
  823. startTime, err := time.ParseInLocation("2006-01-02 15:04:05", courseRunLog.RunDateTime, time.Local)
  824. if err != nil {
  825. c.Data["json"] = map[string]interface{}{
  826. "code": 500,
  827. "msg": "解析课程开始时间失败: " + err.Error(),
  828. "data": map[string]interface{}{
  829. "runStatus": 0,
  830. },
  831. }
  832. c.ServeJSON()
  833. return
  834. }
  835. // 获取当前时间
  836. now := time.Now()
  837. fmt.Printf("目标时间: %v\n", startTime)
  838. fmt.Printf("当前时间: %v\n", now)
  839. // 计算时间差(秒)
  840. diff := int(now.Sub(startTime).Seconds()) - courseRunLog.PauseTotalTime
  841. courseRunLog.CourseTime = diff
  842. }
  843. // 创建角色ID到角色名称的映射
  844. roleMap := make(map[int]string)
  845. for _, role := range roleInfos {
  846. roleMap[role.RoleID] = role.RoleName
  847. }
  848. // 生成HTML内容
  849. htmlContent := c.generateHTMLContent(courseRunLog, evaLogs, roleMap)
  850. // 创建临时HTML文件
  851. htmlFile, err := os.CreateTemp("", "course_evaluation_report_*.html")
  852. if err != nil {
  853. c.Data["json"] = map[string]interface{}{
  854. "code": 500,
  855. "msg": "创建HTML文件失败: " + err.Error(),
  856. }
  857. c.ServeJSON()
  858. return
  859. }
  860. defer os.Remove(htmlFile.Name())
  861. // 写入HTML内容
  862. _, err = htmlFile.WriteString(htmlContent)
  863. if err != nil {
  864. htmlFile.Close()
  865. c.Data["json"] = map[string]interface{}{
  866. "code": 500,
  867. "msg": "写入HTML文件失败: " + err.Error(),
  868. }
  869. c.ServeJSON()
  870. return
  871. }
  872. htmlFile.Close()
  873. // 创建临时PDF文件
  874. pdfFile, err := os.CreateTemp("", "course_evaluation_report_*.pdf")
  875. if err != nil {
  876. c.Data["json"] = map[string]interface{}{
  877. "code": 500,
  878. "msg": "创建PDF文件失败: " + err.Error(),
  879. }
  880. c.ServeJSON()
  881. return
  882. }
  883. defer os.Remove(pdfFile.Name())
  884. pdfFile.Close()
  885. // 使用wkhtmltopdf将HTML转换为PDF
  886. cmd := exec.Command("wkhtmltopdf", "--encoding", "utf-8", htmlFile.Name(), pdfFile.Name())
  887. var stderr bytes.Buffer
  888. cmd.Stderr = &stderr
  889. err = cmd.Run()
  890. if err != nil {
  891. c.Data["json"] = map[string]interface{}{
  892. "code": 500,
  893. "msg": "HTML转PDF失败: " + err.Error() + "; " + stderr.String(),
  894. }
  895. c.ServeJSON()
  896. return
  897. }
  898. // 设置响应头,返回PDF文件
  899. c.Ctx.Output.Header("Content-Type", "application/pdf")
  900. c.Ctx.Output.Header("Content-Disposition", fmt.Sprintf("attachment; filename=course_evaluation_report_%d.pdf", req.RunLogID))
  901. // 读取PDF文件并写入响应
  902. pdfData, err := ioutil.ReadFile(pdfFile.Name())
  903. if err != nil {
  904. c.Data["json"] = map[string]interface{}{
  905. "code": 500,
  906. "msg": "读取PDF文件失败: " + err.Error(),
  907. }
  908. c.ServeJSON()
  909. return
  910. }
  911. c.Ctx.ResponseWriter.Write(pdfData)
  912. c.Data["json"] = map[string]interface{}{
  913. "code": 200,
  914. "msg": "导出成功",
  915. }
  916. c.ServeJSON()
  917. }
  918. // 生成HTML内容
  919. func (c *EvaLogController) generateHTMLContent(courseRunLog models.CourseRunLog, evaLogs []models.CourseEvaLog, roleMap map[int]string) string {
  920. var buf bytes.Buffer
  921. // 添加HTML头部
  922. buf.WriteString(`<!DOCTYPE html>
  923. <html>
  924. <head>
  925. <meta charset="UTF-8">
  926. <title>课程电子考评表</title>
  927. <style>
  928. body {
  929. font-family: "Microsoft YaHei", sans-serif;
  930. margin: 20px;
  931. }
  932. .header {
  933. text-align: center;
  934. font-size: 24px;
  935. font-weight: bold;
  936. margin-bottom: 20px;
  937. }
  938. .info-table {
  939. width: 100%;
  940. border-collapse: collapse;
  941. margin-bottom: 20px;
  942. }
  943. .info-table td {
  944. padding: 5px 10px;
  945. }
  946. .info-table .label {
  947. font-weight: bold;
  948. width: 20%;
  949. }
  950. .eva-table {
  951. width: 100%;
  952. border-collapse: collapse;
  953. margin-bottom: 20px;
  954. }
  955. .eva-table th, .eva-table td {
  956. border: 1px solid #000;
  957. padding: 8px;
  958. text-align: center;
  959. }
  960. .eva-table th {
  961. background-color: #f0f0f0;
  962. font-weight: bold;
  963. }
  964. .eva-table th:nth-child(4),
  965. .eva-table td:nth-child(4) {
  966. width: 200px; /* 或使用百分比 width: 25%; */
  967. max-width: 300px;
  968. text-align: left;
  969. }
  970. .footer {
  971. text-align: left;
  972. font-style: italic;
  973. margin-top: 20px;
  974. }
  975. </style>
  976. </head>
  977. <body>
  978. <div class="header">课程电子考评表</div>
  979. <table class="info-table">
  980. <tr>
  981. <td class="label">运行课程名称:</td>
  982. <td>` + courseRunLog.RunCourseName + `</td>
  983. <td class="label">开课时间:</td>
  984. <td>` + courseRunLog.RunDateTime + `</td>
  985. </tr>
  986. <tr>
  987. <td class="label">课程用时:</td>
  988. <td>` + formatSecondsToHMS(courseRunLog.CourseTime) + `</td>
  989. <td class="label">课程得分:</td>
  990. <td>` + fmt.Sprintf("%.2f", courseRunLog.CourseScore) + `</td>
  991. </tr>
  992. <tr>
  993. <td class="label">暂停累计时长(秒):</td>
  994. <td>` + fmt.Sprintf("%d", courseRunLog.PauseTotalTime) + `</td>
  995. <td class="label">课程状态:</td>
  996. <td>`)
  997. // 添加状态信息
  998. switch courseRunLog.PauseFlag {
  999. case -1:
  1000. buf.WriteString("已结束")
  1001. case 0:
  1002. buf.WriteString("运行中")
  1003. case 1:
  1004. buf.WriteString("已暂停")
  1005. }
  1006. buf.WriteString(`</td>
  1007. </tr>
  1008. </table>
  1009. <table class="eva-table">
  1010. <thead>
  1011. <tr>
  1012. <th>序号</th>
  1013. <th>操作时间</th>
  1014. <th>角色名称</th>
  1015. <th>评价名称</th>
  1016. <th>评价类型</th>
  1017. <th>赋分</th>
  1018. <th>失分</th>
  1019. <th>得分</th>
  1020. <th>来源</th>
  1021. <th>是否操作</th>
  1022. </tr>
  1023. </thead>
  1024. <tbody>`)
  1025. // 表格数据
  1026. for _, evaLog := range evaLogs {
  1027. // 处理评价目标角色名称
  1028. targetRoleName := roleMap[evaLog.EvaTargetRole]
  1029. if targetRoleName == "" {
  1030. targetRoleName = fmt.Sprintf("角色%d", evaLog.EvaTargetRole)
  1031. }
  1032. // 处理操作时间 - 改为 hh:mm:ss 格式
  1033. hours := evaLog.OperTime / 3600
  1034. minutes := (evaLog.OperTime % 3600) / 60
  1035. seconds := evaLog.OperTime % 60
  1036. operTimeStr := fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
  1037. // 处理评价类型
  1038. evaType := "主观"
  1039. if evaLog.EvaType == 1 {
  1040. evaType = "客观"
  1041. }
  1042. // 处理评价来源
  1043. source := "流程图"
  1044. if evaLog.EvaSource == 1 {
  1045. source = "教员自定义"
  1046. }
  1047. // 处理是否操作
  1048. isOperated := "未操作"
  1049. if evaLog.IsOperated == 1 {
  1050. isOperated = "已操作"
  1051. }
  1052. buf.WriteString("<tr>")
  1053. buf.WriteString("<td>" + fmt.Sprintf("%d", evaLog.SeqNo) + "</td>")
  1054. buf.WriteString("<td>" + operTimeStr + "</td>")
  1055. buf.WriteString("<td>" + targetRoleName + "</td>")
  1056. buf.WriteString("<td style='text-align: left;'>" + evaLog.EvaName + "</td>")
  1057. buf.WriteString("<td>" + evaType + "</td>")
  1058. buf.WriteString("<td>" + fmt.Sprintf("%d", evaLog.EvaScore) + "</td>")
  1059. buf.WriteString("<td>" + fmt.Sprintf("%d", evaLog.EvaDeduction) + "</td>")
  1060. buf.WriteString("<td>" + fmt.Sprintf("%.1f", evaLog.OperScore) + "</td>")
  1061. buf.WriteString("<td>" + source + "</td>")
  1062. buf.WriteString("<td>" + isOperated + "</td>")
  1063. buf.WriteString("</tr>")
  1064. }
  1065. buf.WriteString(`</tbody>
  1066. </table>
  1067. <div class="footer">导出时间: ` + time.Now().Format("2006-01-02 15:04:05") + `</div>
  1068. </body>
  1069. </html>`)
  1070. return buf.String()
  1071. }
  1072. // /evalog/operRunRecordExport 导出操作记录
  1073. func (c *EvaLogController) OperRunRecordExport() {
  1074. type Request struct {
  1075. RunLogID int `json:"runLogID"`
  1076. }
  1077. var req Request
  1078. requestBody := c.Ctx.Input.RequestBody
  1079. println("Request Body:", string(requestBody))
  1080. if err := json.Unmarshal(requestBody, &req); err != nil {
  1081. c.Ctx.Output.SetStatus(400)
  1082. c.Data["json"] = map[string]interface{}{
  1083. "code": 400,
  1084. "msg": "请求参数格式错误: " + err.Error(),
  1085. }
  1086. c.ServeJSON()
  1087. return
  1088. }
  1089. if req.RunLogID == 0 {
  1090. c.Ctx.Output.SetStatus(400)
  1091. c.Data["json"] = map[string]interface{}{
  1092. "code": 400,
  1093. "msg": "缺少运行记录ID参数",
  1094. }
  1095. c.ServeJSON()
  1096. return
  1097. }
  1098. o := orm.NewOrm()
  1099. // 查询课程运行记录信息
  1100. var courseRunLog models.CourseRunLog
  1101. err := o.Raw(`SELECT * FROM CourseRunLog WHERE 运行记录编号 = ?`, req.RunLogID).QueryRow(&courseRunLog)
  1102. if err != nil {
  1103. if err == orm.ErrNoRows {
  1104. c.Data["json"] = map[string]interface{}{
  1105. "code": 404,
  1106. "msg": "未找到对应的课程运行记录",
  1107. }
  1108. } else {
  1109. c.Data["json"] = map[string]interface{}{
  1110. "code": 500,
  1111. "msg": "查询课程运行记录失败: " + err.Error(),
  1112. }
  1113. }
  1114. c.ServeJSON()
  1115. return
  1116. }
  1117. // 查询该课程运行记录下的所有操作记录项
  1118. var operLogs []models.CourseOperLog
  1119. _, err = o.Raw(`
  1120. SELECT *
  1121. FROM CourseOperLog
  1122. WHERE 运行记录编号 = ?
  1123. ORDER BY 操作时间
  1124. `, req.RunLogID).QueryRows(&operLogs)
  1125. if err != nil {
  1126. c.Data["json"] = map[string]interface{}{
  1127. "code": 500,
  1128. "msg": "查询操作记录列表失败: " + err.Error(),
  1129. }
  1130. c.ServeJSON()
  1131. return
  1132. }
  1133. // 生成HTML内容
  1134. htmlContent := c.generateOperHTMLContent(courseRunLog, operLogs)
  1135. // 创建临时HTML文件
  1136. htmlFile, err := ioutil.TempFile("", "course_operation_report_*.html")
  1137. if err != nil {
  1138. c.Data["json"] = map[string]interface{}{
  1139. "code": 500,
  1140. "msg": "创建HTML文件失败: " + err.Error(),
  1141. }
  1142. c.ServeJSON()
  1143. return
  1144. }
  1145. defer os.Remove(htmlFile.Name())
  1146. // 写入HTML内容
  1147. _, err = htmlFile.WriteString(htmlContent)
  1148. if err != nil {
  1149. htmlFile.Close()
  1150. c.Data["json"] = map[string]interface{}{
  1151. "code": 500,
  1152. "msg": "写入HTML文件失败: " + err.Error(),
  1153. }
  1154. c.ServeJSON()
  1155. return
  1156. }
  1157. htmlFile.Close()
  1158. // 创建临时PDF文件
  1159. pdfFile, err := ioutil.TempFile("", "course_operation_report_*.pdf")
  1160. if err != nil {
  1161. c.Data["json"] = map[string]interface{}{
  1162. "code": 500,
  1163. "msg": "创建PDF文件失败: " + err.Error(),
  1164. }
  1165. c.ServeJSON()
  1166. return
  1167. }
  1168. defer os.Remove(pdfFile.Name())
  1169. pdfFile.Close()
  1170. // 使用wkhtmltopdf将HTML转换为PDF
  1171. cmd := exec.Command("wkhtmltopdf", "--encoding", "utf-8", htmlFile.Name(), pdfFile.Name())
  1172. var stderr bytes.Buffer
  1173. cmd.Stderr = &stderr
  1174. err = cmd.Run()
  1175. if err != nil {
  1176. c.Data["json"] = map[string]interface{}{
  1177. "code": 500,
  1178. "msg": "HTML转PDF失败: " + err.Error() + "; " + stderr.String(),
  1179. }
  1180. c.ServeJSON()
  1181. return
  1182. }
  1183. // 设置响应头,返回PDF文件
  1184. c.Ctx.Output.Header("Content-Type", "application/pdf")
  1185. c.Ctx.Output.Header("Content-Disposition", fmt.Sprintf("attachment; filename=course_operation_report_%d.pdf", req.RunLogID))
  1186. // 读取PDF文件并写入响应
  1187. pdfData, err := ioutil.ReadFile(pdfFile.Name())
  1188. if err != nil {
  1189. c.Data["json"] = map[string]interface{}{
  1190. "code": 500,
  1191. "msg": "读取PDF文件失败: " + err.Error(),
  1192. }
  1193. c.ServeJSON()
  1194. return
  1195. }
  1196. c.Ctx.ResponseWriter.Write(pdfData)
  1197. c.Data["json"] = map[string]interface{}{
  1198. "code": 200,
  1199. "msg": "导出成功",
  1200. }
  1201. c.ServeJSON()
  1202. }
  1203. // 生成操作记录HTML内容
  1204. func (c *EvaLogController) generateOperHTMLContent(courseRunLog models.CourseRunLog, operLogs []models.CourseOperLog) string {
  1205. var buf bytes.Buffer
  1206. // 添加HTML头部
  1207. buf.WriteString(`<!DOCTYPE html>
  1208. <html>
  1209. <head>
  1210. <meta charset="UTF-8">
  1211. <title>课程操作记录表</title>
  1212. <style>
  1213. body {
  1214. font-family: "Microsoft YaHei", sans-serif;
  1215. margin: 20px;
  1216. }
  1217. .header {
  1218. text-align: center;
  1219. font-size: 24px;
  1220. font-weight: bold;
  1221. margin-bottom: 20px;
  1222. }
  1223. .info-table {
  1224. width: 100%;
  1225. border-collapse: collapse;
  1226. margin-bottom: 20px;
  1227. }
  1228. .info-table td {
  1229. padding: 5px 10px;
  1230. }
  1231. .info-table .label {
  1232. font-weight: bold;
  1233. width: 20%;
  1234. }
  1235. .oper-table {
  1236. width: 100%;
  1237. border-collapse: collapse;
  1238. margin-bottom: 20px;
  1239. }
  1240. .oper-table th, .oper-table td {
  1241. border: 1px solid #000;
  1242. padding: 8px;
  1243. text-align: center;
  1244. }
  1245. .oper-table th {
  1246. background-color: #f0f0f0;
  1247. font-weight: bold;
  1248. }
  1249. .footer {
  1250. text-align: left;
  1251. font-style: italic;
  1252. margin-top: 20px;
  1253. }
  1254. </style>
  1255. </head>
  1256. <body>
  1257. <div class="header">课程操作记录表</div>
  1258. <table class="info-table">
  1259. <tr>
  1260. <td class="label">运行课程名称:</td>
  1261. <td>` + courseRunLog.RunCourseName + `</td>
  1262. <td class="label">开课时间:</td>
  1263. <td>` + courseRunLog.RunDateTime + `</td>
  1264. </tr>
  1265. <tr>
  1266. <td class="label">课程用时:</td>
  1267. <td>` + formatSecondsToHMS(courseRunLog.CourseTime) + `</td>
  1268. <td class="label">课程得分:</td>
  1269. <td>` + fmt.Sprintf("%.2f", courseRunLog.CourseScore) + `</td>
  1270. </tr>
  1271. <tr>
  1272. <td class="label">暂停累计时长(秒):</td>
  1273. <td>` + fmt.Sprintf("%d", courseRunLog.PauseTotalTime) + `</td>
  1274. <td class="label">课程状态:</td>
  1275. <td>`)
  1276. // 添加状态信息
  1277. switch courseRunLog.PauseFlag {
  1278. case -1:
  1279. buf.WriteString("已结束")
  1280. case 0:
  1281. buf.WriteString("运行中")
  1282. case 1:
  1283. buf.WriteString("已暂停")
  1284. }
  1285. buf.WriteString(`</td>
  1286. </tr>
  1287. </table>
  1288. <table class="oper-table">
  1289. <thead>
  1290. <tr>
  1291. <th>序号</th>
  1292. <th>操作时间</th>
  1293. <th>操作名称</th>
  1294. <th>操作得分</th>
  1295. <th>操作参数1</th>
  1296. <th>操作参数2</th>
  1297. <th>操作参数3</th>
  1298. <th>操作参数4</th>
  1299. <th>操作参数5</th>
  1300. </tr>
  1301. </thead>
  1302. <tbody>`)
  1303. // 表格数据
  1304. for _, operLog := range operLogs {
  1305. // 处理操作时间 - 改为 hh:mm:ss 格式
  1306. hours := operLog.OperTime / 3600
  1307. minutes := (operLog.OperTime % 3600) / 60
  1308. seconds := operLog.OperTime % 60
  1309. operTimeStr := fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
  1310. buf.WriteString("<tr>")
  1311. buf.WriteString("<td>" + fmt.Sprintf("%d", operLog.RunOperID) + "</td>")
  1312. buf.WriteString("<td>" + operTimeStr + "</td>")
  1313. buf.WriteString("<td style='text-align: left;'>" + operLog.OperName + "</td>")
  1314. buf.WriteString("<td>" + fmt.Sprintf("%.1f", operLog.OperScore) + "</td>")
  1315. buf.WriteString("<td>" + operLog.OperParam1 + "</td>")
  1316. buf.WriteString("<td>" + operLog.OperParam2 + "</td>")
  1317. buf.WriteString("<td>" + operLog.OperParam3 + "</td>")
  1318. buf.WriteString("<td>" + operLog.OperParam4 + "</td>")
  1319. buf.WriteString("<td>" + operLog.OperParam5 + "</td>")
  1320. buf.WriteString("</tr>")
  1321. }
  1322. buf.WriteString(`</tbody>
  1323. </table>
  1324. <div class="footer">导出时间: ` + time.Now().Format("2006-01-02 15:04:05") + `</div>
  1325. </body>
  1326. </html>`)
  1327. return buf.String()
  1328. }
  1329. // /执行上传对应电子考评表到信息还系统
  1330. func DoUploadTMS(RunLogID int) error {
  1331. o := orm.NewOrm()
  1332. // 查询课程运行记录信息
  1333. var courseRunLog models.CourseRunLog
  1334. err := o.Raw(`SELECT * FROM CourseRunLog WHERE 运行记录编号 = ?`, RunLogID).QueryRow(&courseRunLog)
  1335. if err != nil {
  1336. return fmt.Errorf("查询课程运行记录失败: %v", err)
  1337. }
  1338. // 查询该课程运行记录下的所有评价项
  1339. var evaLogs []models.CourseEvaLog
  1340. _, err = o.Raw(`
  1341. SELECT *
  1342. FROM CourseEvaLog
  1343. WHERE 运行记录编号 = ?
  1344. ORDER BY 序号
  1345. `, RunLogID).QueryRows(&evaLogs)
  1346. if err != nil {
  1347. return fmt.Errorf("查询评价记录列表失败: %v", err)
  1348. }
  1349. // 查询所有角色信息用于关联角色名称
  1350. var roleInfos []*models.RoleInfo
  1351. _, err = o.QueryTable(new(models.RoleInfo)).All(&roleInfos)
  1352. if err != nil {
  1353. return fmt.Errorf("查询角色信息失败: %v", err)
  1354. }
  1355. // 创建角色ID到角色名称的映射
  1356. roleMap := make(map[int]string)
  1357. for _, role := range roleInfos {
  1358. roleMap[role.RoleID] = role.RoleName
  1359. }
  1360. // 生成HTML内容
  1361. controller := &EvaLogController{}
  1362. htmlContent := controller.generateHTMLContent(courseRunLog, evaLogs, roleMap)
  1363. // 创建临时HTML文件
  1364. htmlFile, err := os.CreateTemp("", "course_evaluation_report_*.html")
  1365. if err != nil {
  1366. return fmt.Errorf("创建HTML文件失败: %v", err)
  1367. }
  1368. defer os.Remove(htmlFile.Name())
  1369. // 写入HTML内容
  1370. _, err = htmlFile.WriteString(htmlContent)
  1371. if err != nil {
  1372. htmlFile.Close()
  1373. return fmt.Errorf("写入HTML文件失败: %v", err)
  1374. }
  1375. htmlFile.Close()
  1376. // 创建临时PDF文件
  1377. pdfFile, err := os.CreateTemp("", "course_evaluation_report_*.pdf")
  1378. if err != nil {
  1379. return fmt.Errorf("创建PDF文件失败: %v", err)
  1380. }
  1381. defer os.Remove(pdfFile.Name())
  1382. pdfFile.Close()
  1383. // 使用wkhtmltopdf将HTML转换为PDF
  1384. cmd := exec.Command("wkhtmltopdf", "--encoding", "utf-8", htmlFile.Name(), pdfFile.Name())
  1385. var stderr bytes.Buffer
  1386. cmd.Stderr = &stderr
  1387. err = cmd.Run()
  1388. if err != nil {
  1389. return fmt.Errorf("HTML转PDF失败: %v; %s", err, stderr.String())
  1390. }
  1391. // 准备上传到信息化系统的数据
  1392. // 学员GUID(可能有多个,用逗号分隔)
  1393. traineeGUIDs := courseRunLog.TraineeGUID
  1394. // 课程得分
  1395. score := fmt.Sprintf("%.2f", courseRunLog.CourseScore)
  1396. // 构造请求URL
  1397. url := common.GetAPIEndpoint(common.SVR_API_UPLOAD_AbilityScore)
  1398. // 创建multipart表单数据
  1399. var buf bytes.Buffer
  1400. writer := multipart.NewWriter(&buf)
  1401. // 添加表单字段
  1402. _ = writer.WriteField("PXJLGUID", courseRunLog.TrainingRecordGUID)
  1403. _ = writer.WriteField("XYEMPID", traineeGUIDs)
  1404. _ = writer.WriteField("DF", score)
  1405. // 添加文件字段
  1406. fileWriter, _ := writer.CreateFormFile("FILE", fmt.Sprintf("course_evaluation_report_%d.pdf", RunLogID))
  1407. pdfData, _ := ioutil.ReadFile(pdfFile.Name())
  1408. _, _ = fileWriter.Write(pdfData)
  1409. // 添加Content-Type字段
  1410. _ = writer.WriteField("Content-Type", "multipart/form-data")
  1411. // 关闭multipart writer
  1412. writer.Close()
  1413. // 创建请求
  1414. req, err := http.NewRequest("POST", url, &buf)
  1415. if err != nil {
  1416. return fmt.Errorf("创建请求失败: %v", err)
  1417. }
  1418. // 设置请求头
  1419. req.Header.Set("Content-Type", writer.FormDataContentType())
  1420. // 从配置中获取token
  1421. token, err := beego.AppConfig.String("SYSTOKEN")
  1422. if err == nil && token != "" {
  1423. req.Header.Set("token", token)
  1424. }
  1425. // 发送请求
  1426. client := &http.Client{}
  1427. resp, err := client.Do(req)
  1428. if err != nil {
  1429. return fmt.Errorf("发送请求失败: %v", err)
  1430. }
  1431. defer resp.Body.Close()
  1432. // 读取响应
  1433. body, err := ioutil.ReadAll(resp.Body)
  1434. if err != nil {
  1435. return fmt.Errorf("读取响应失败: %v", err)
  1436. }
  1437. // 检查状态码
  1438. if resp.StatusCode != http.StatusOK {
  1439. return fmt.Errorf("信息化系统返回错误状态码: %d, 响应内容: %s", resp.StatusCode, string(body))
  1440. }
  1441. // 解析响应
  1442. var result struct {
  1443. ResultCode string `json:"ResultCode"`
  1444. ResultDesc string `json:"ResultDesc"`
  1445. }
  1446. err = json.Unmarshal(body, &result)
  1447. if err != nil {
  1448. return fmt.Errorf("解析响应失败: %v", err)
  1449. }
  1450. // 检查结果
  1451. if result.ResultCode != "0" {
  1452. return fmt.Errorf("信息化系统处理失败: %s", result.ResultDesc)
  1453. }
  1454. return nil
  1455. }
  1456. // /evalog/uploadtms 课程评价记录上传信息化系统
  1457. func (c *EvaLogController) UploadTMS() {
  1458. type Request struct {
  1459. RunLogID int `json:"runLogID"`
  1460. }
  1461. var req Request
  1462. requestBody := c.Ctx.Input.RequestBody
  1463. println("Request Body:", string(requestBody))
  1464. if err := json.Unmarshal(requestBody, &req); err != nil {
  1465. c.Ctx.Output.SetStatus(400)
  1466. c.Data["json"] = map[string]interface{}{
  1467. "code": 400,
  1468. "msg": "请求参数格式错误: " + err.Error(),
  1469. }
  1470. c.ServeJSON()
  1471. return
  1472. }
  1473. if req.RunLogID == 0 {
  1474. c.Ctx.Output.SetStatus(400)
  1475. c.Data["json"] = map[string]interface{}{
  1476. "code": 400,
  1477. "msg": "缺少运行记录ID参数",
  1478. }
  1479. c.ServeJSON()
  1480. return
  1481. }
  1482. err := DoUploadTMS(req.RunLogID)
  1483. if err != nil {
  1484. c.Data["json"] = map[string]interface{}{
  1485. "code": 500,
  1486. "msg": "上传信息还系统失败: " + err.Error(),
  1487. }
  1488. c.ServeJSON()
  1489. return
  1490. }
  1491. c.Data["json"] = map[string]interface{}{
  1492. "code": 200,
  1493. "msg": "上传信息还系统成功",
  1494. }
  1495. c.ServeJSON()
  1496. }
  1497. // /evalog/setscore 设置课程得分
  1498. // /evalog/setscore 设置课程得分
  1499. func (c *EvaLogController) SetScore() {
  1500. type Request struct {
  1501. RunLogID int `json:"runLogID"`
  1502. SeqNo int `json:"seqNo"`
  1503. EvaScore int `json:"evaScore"`
  1504. }
  1505. var req Request
  1506. requestBody := c.Ctx.Input.RequestBody
  1507. println("Request Body:", string(requestBody))
  1508. if err := json.Unmarshal(requestBody, &req); err != nil {
  1509. c.Ctx.Output.SetStatus(400)
  1510. c.Data["json"] = map[string]interface{}{
  1511. "code": 400,
  1512. "msg": "请求参数格式错误: " + err.Error(),
  1513. }
  1514. c.ServeJSON()
  1515. return
  1516. }
  1517. if req.RunLogID == 0 || req.SeqNo == 0 {
  1518. c.Ctx.Output.SetStatus(400)
  1519. c.Data["json"] = map[string]interface{}{
  1520. "code": 400,
  1521. "msg": "缺少必要参数",
  1522. }
  1523. c.ServeJSON()
  1524. return
  1525. }
  1526. o := orm.NewOrm()
  1527. // 查询对应的评价记录
  1528. var evaLog models.CourseEvaLog
  1529. err := o.Raw(`
  1530. SELECT *
  1531. FROM CourseEvaLog
  1532. WHERE 运行记录编号 = ? AND 序号 = ?
  1533. `, req.RunLogID, req.SeqNo).QueryRow(&evaLog)
  1534. if err != nil {
  1535. if err == orm.ErrNoRows {
  1536. c.Data["json"] = map[string]interface{}{
  1537. "code": 404,
  1538. "msg": "未找到对应的评价记录",
  1539. }
  1540. } else {
  1541. c.Data["json"] = map[string]interface{}{
  1542. "code": 500,
  1543. "msg": "查询评价记录失败: " + err.Error(),
  1544. }
  1545. }
  1546. c.ServeJSON()
  1547. return
  1548. }
  1549. // 验证分数范围应在0到评价赋分之间
  1550. if req.EvaScore < 0 || req.EvaScore > evaLog.EvaScore {
  1551. c.Data["json"] = map[string]interface{}{
  1552. "code": 400,
  1553. "msg": fmt.Sprintf("分数应在0到%d之间", evaLog.EvaScore),
  1554. }
  1555. c.ServeJSON()
  1556. return
  1557. }
  1558. operScore := req.EvaScore
  1559. // 更新CourseEvaLog表中的操作得分
  1560. _, err = o.Raw(`
  1561. UPDATE CourseEvaLog
  1562. SET 操作得分 = ?
  1563. WHERE 运行记录编号 = ? AND 序号 = ?
  1564. `, operScore, req.RunLogID, req.SeqNo).Exec()
  1565. if err != nil {
  1566. c.Data["json"] = map[string]interface{}{
  1567. "code": 500,
  1568. "msg": "更新评价分数失败: " + err.Error(),
  1569. }
  1570. c.ServeJSON()
  1571. return
  1572. }
  1573. // 重新计算整个课程的总得分
  1574. var totalScore float64
  1575. err = o.Raw(`
  1576. SELECT COALESCE(SUM(操作得分), 0)
  1577. FROM CourseEvaLog
  1578. WHERE 运行记录编号 = ?
  1579. `, req.RunLogID).QueryRow(&totalScore)
  1580. if err != nil {
  1581. c.Data["json"] = map[string]interface{}{
  1582. "code": 500,
  1583. "msg": "计算总得分失败: " + err.Error(),
  1584. }
  1585. c.ServeJSON()
  1586. return
  1587. }
  1588. // 更新课程运行日志中的总得分
  1589. _, err = o.Raw(`
  1590. UPDATE CourseRunLog
  1591. SET 课程得分 = ?
  1592. WHERE 运行记录编号 = ?
  1593. `, totalScore, req.RunLogID).Exec()
  1594. if err != nil {
  1595. c.Data["json"] = map[string]interface{}{
  1596. "code": 500,
  1597. "msg": "更新总得分失败: " + err.Error(),
  1598. }
  1599. c.ServeJSON()
  1600. return
  1601. }
  1602. c.Data["json"] = map[string]interface{}{
  1603. "code": 200,
  1604. "msg": "设置评价分数成功",
  1605. "data": map[string]interface{}{
  1606. "runLogID": req.RunLogID,
  1607. "seqNo": req.SeqNo,
  1608. "evaScore": req.EvaScore,
  1609. "operScore": operScore,
  1610. "totalScore": totalScore,
  1611. },
  1612. }
  1613. c.ServeJSON()
  1614. }