operation.go 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351
  1. package opsdev
  2. import (
  3. "context"
  4. "fmt"
  5. "regexp"
  6. "strings"
  7. "dashoo.cn/common_definition/comm_def"
  8. "dashoo.cn/opms_libary/myerrors"
  9. opsdevdao "dashoo.cn/opms_parent/app/dao/opsdev"
  10. opsdevmodel "dashoo.cn/opms_parent/app/model/opsdev"
  11. "dashoo.cn/opms_parent/app/service"
  12. "github.com/gogf/gf/database/gdb"
  13. "github.com/gogf/gf/frame/g"
  14. "github.com/gogf/gf/os/gtime"
  15. "github.com/gogf/gf/util/gconv"
  16. "github.com/gogf/gf/util/gvalid"
  17. )
  18. // eventTypeToDevTaskType 运维事件类型 → 研发任务类型映射
  19. var eventTypeToDevTaskType = map[string]string{
  20. "10": "20", // 操作咨询 → 功能开发
  21. "20": "21", // 数据处理 → 数据处理
  22. "30": "35", // 系统BUG → BUG
  23. "40": "20", // 功能调整 → 功能开发
  24. "50": "20", // 二开需求 → 功能开发
  25. "90": "20", // 其他问题 → 功能开发
  26. }
  27. type OperationService struct {
  28. *service.ContextService
  29. Dao *opsdevdao.OpsOperationEventDao
  30. RecordDao *opsdevdao.OpsOperationEventRecordDao
  31. AttachmentDao *opsdevdao.OpsOperationEventAttachmentDao
  32. DeliveryProjectAttachmentDao *opsdevdao.OpsDeliveryProjectEventAttachmentDao
  33. WorkHourDao *opsdevdao.OpsOperationWorkHourDao
  34. DeliveryProjectDao *opsdevdao.OpsDeliveryProjectDao
  35. }
  36. func NewOperationService(ctx context.Context) (svc *OperationService, err error) {
  37. svc = new(OperationService)
  38. if svc.ContextService, err = svc.Init(ctx); err != nil {
  39. return nil, err
  40. }
  41. svc.Dao = opsdevdao.NewOpsOperationEventDao(svc.Tenant)
  42. svc.RecordDao = opsdevdao.NewOpsOperationEventRecordDao(svc.Tenant)
  43. svc.AttachmentDao = opsdevdao.NewOpsOperationEventAttachmentDao(svc.Tenant)
  44. svc.DeliveryProjectAttachmentDao = opsdevdao.NewOpsDeliveryProjectEventAttachmentDao(svc.Tenant)
  45. svc.WorkHourDao = opsdevdao.NewOpsOperationWorkHourDao(svc.Tenant)
  46. svc.DeliveryProjectDao = opsdevdao.NewOpsDeliveryProjectDao(svc.Tenant)
  47. return svc, nil
  48. }
  49. // GetList 运维事件列表
  50. func (s *OperationService) GetList(req *opsdevmodel.OpsOperationEventSearchReq) (total int, list []*opsdevmodel.OpsOperationEvent, err error) {
  51. db := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).Where(s.Dao.Columns.EventStatus+" != ?", opsdevmodel.EventStatusClosed)
  52. if req.EventNo != "" {
  53. db = db.Where(s.Dao.Columns.EventNo, req.EventNo)
  54. }
  55. if req.EventTitle != "" {
  56. db = db.Where(s.Dao.Columns.EventTitle+" like ?", "%"+req.EventTitle+"%")
  57. }
  58. if req.EventStatus != "" {
  59. db = db.Where(s.Dao.Columns.EventStatus, req.EventStatus)
  60. }
  61. if req.EventType != "" {
  62. db = db.Where(s.Dao.Columns.EventType, req.EventType)
  63. }
  64. if req.PriorityLevel != "" {
  65. db = db.Where(s.Dao.Columns.PriorityLevel, req.PriorityLevel)
  66. }
  67. if req.IsBig != "" {
  68. db = db.Where(s.Dao.Columns.IsBig, req.IsBig)
  69. }
  70. if req.IsOps != "" {
  71. db = db.Where(s.Dao.Columns.IsOps, req.IsOps)
  72. }
  73. if req.CustName != "" {
  74. db = db.Where(s.Dao.Columns.CustName+" like ?", "%"+req.CustName+"%")
  75. }
  76. if req.OpsUserName != "" {
  77. db = db.Where(s.Dao.Columns.OpsUserName+" like ?", "%"+req.OpsUserName+"%")
  78. }
  79. if req.ContractName != "" {
  80. db = db.Where(s.Dao.Columns.ContractName+" like ?", "%"+req.ContractName+"%")
  81. }
  82. if req.ProductLine != "" {
  83. db = db.Where(s.Dao.Columns.ProductLine, req.ProductLine)
  84. }
  85. if req.BeginTime != "" {
  86. db = db.Where(s.Dao.Columns.CreatedTime+" >= ?", req.BeginTime)
  87. }
  88. if req.EndTime != "" {
  89. db = db.Where(s.Dao.Columns.CreatedTime+" <= ?", req.EndTime)
  90. }
  91. total, err = db.Count()
  92. if err != nil {
  93. g.Log().Error(err)
  94. return 0, nil, myerrors.DbError("查询运维事件数量失败")
  95. }
  96. pageNum, pageSize := req.GetPage()
  97. err = db.Page(pageNum, pageSize).Order(s.Dao.Columns.CreatedTime + " desc").Scan(&list)
  98. if err != nil {
  99. g.Log().Error(err)
  100. return 0, nil, myerrors.DbError("查询运维事件列表失败")
  101. }
  102. return total, list, nil
  103. }
  104. // GetEntityById 运维事件详情
  105. func (s *OperationService) GetEntityById(req *comm_def.IdReq) (detail *opsdevmodel.OpsOperationEvent, err error) {
  106. if req.Id <= 0 {
  107. return nil, myerrors.ValidError("参数有误!")
  108. }
  109. detail = new(opsdevmodel.OpsOperationEvent)
  110. err = s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).WherePri(s.Dao.Columns.Id, req.Id).Scan(detail)
  111. if err != nil {
  112. g.Log().Error(err)
  113. return nil, myerrors.DbError("查询运维事件详情失败")
  114. }
  115. if detail.Id <= 0 {
  116. return nil, myerrors.TipsError("运维事件不存在")
  117. }
  118. return detail, nil
  119. }
  120. // Create 创建运维事件
  121. func (s *OperationService) Create(req *opsdevmodel.OpsOperationEventReq) (err error) {
  122. if err = gvalid.CheckStruct(s.Ctx, req, nil); err != nil {
  123. return myerrors.ValidError(err.Error())
  124. }
  125. data := gconv.Map(req)
  126. service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  127. data["eventNo"] = s.generateEventNo()
  128. data["eventStatus"] = opsdevmodel.EventStatusProcessing
  129. data["opsUserId"] = s.GetCxtUserId()
  130. data["opsUserName"] = s.GetCxtUserName()
  131. if v, ok := data["feedbackDate"]; ok && v == "" {
  132. data["feedbackDate"] = gtime.Now()
  133. }
  134. if v, ok := data["assignTime"]; ok && v == "" {
  135. data["assignTime"] = gtime.Now()
  136. }
  137. delete(data, "completeTime")
  138. delete(data, "attachments")
  139. result, err := s.Dao.Data(data).Insert()
  140. if err != nil {
  141. g.Log().Error(err)
  142. return myerrors.DbError("创建运维事件失败")
  143. }
  144. _, err = result.LastInsertId()
  145. if err != nil {
  146. g.Log().Error(err)
  147. return myerrors.DbError("获取运维事件ID失败")
  148. }
  149. return nil
  150. }
  151. // AddWorkHour 添加工时记录
  152. func (s *OperationService) AddWorkHour(req *opsdevmodel.OpsOperationWorkHourAddReq) error {
  153. if req.EventId <= 0 {
  154. return myerrors.ValidError("事件ID不能为空")
  155. }
  156. event := new(opsdevmodel.OpsOperationEvent)
  157. err := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).WherePri(s.Dao.Columns.Id, req.EventId).Scan(event)
  158. if err != nil {
  159. g.Log().Error(err)
  160. return myerrors.DbError("查询运维事件失败")
  161. }
  162. if event.Id <= 0 {
  163. return myerrors.TipsError("运维事件不存在")
  164. }
  165. if event.EventStatus != opsdevmodel.EventStatusProcessing && event.EventStatus != opsdevmodel.EventStatusProcessingNormal {
  166. return myerrors.TipsError("只有处理中的事件才能添加工作时长")
  167. }
  168. workDate, err := gtime.StrToTime(req.WorkDate)
  169. if err != nil {
  170. return myerrors.ValidError("工作日期格式错误")
  171. }
  172. today := gtime.Now().Format("Y-m-d")
  173. if workDate.Format("Y-m-d") > today {
  174. return myerrors.ValidError("工作日期不能晚于今天")
  175. }
  176. if req.WorkHour < 0.5 {
  177. return myerrors.ValidError("工作时长不能小于0.5小时")
  178. }
  179. opsUserId := s.GetCxtUserId()
  180. opsUserName := s.GetCxtUserName()
  181. return s.Dao.Transaction(s.Ctx, func(ctx context.Context, tx *gdb.TX) error {
  182. workHourData := g.Map{
  183. s.WorkHourDao.Columns.EventId: req.EventId,
  184. s.WorkHourDao.Columns.OpsUserId: opsUserId,
  185. s.WorkHourDao.Columns.OpsUserName: opsUserName,
  186. s.WorkHourDao.Columns.WorkDate: req.WorkDate,
  187. s.WorkHourDao.Columns.WorkHour: req.WorkHour,
  188. s.WorkHourDao.Columns.Remark: req.Remark,
  189. }
  190. service.SetCreatedInfo(workHourData, opsUserId, opsUserName)
  191. _, err := tx.Model(s.WorkHourDao.Table).Data(workHourData).Insert()
  192. if err != nil {
  193. g.Log().Error(err)
  194. return myerrors.DbError("添加工作时长失败")
  195. }
  196. _, err = tx.Exec("UPDATE ops_operation_event SET total_work_hour = total_work_hour + ? WHERE id = ?", req.WorkHour, req.EventId)
  197. if err != nil {
  198. g.Log().Error(err)
  199. return myerrors.DbError("更新事件总工作时长失败")
  200. }
  201. return nil
  202. })
  203. }
  204. // DeleteAttachment 删除附件
  205. func (s *OperationService) DeleteAttachment(req *comm_def.IdReq) error {
  206. if req.Id <= 0 {
  207. return myerrors.ValidError("参数有误!")
  208. }
  209. attachment := new(opsdevmodel.OpsOperationEventAttachment)
  210. err := s.AttachmentDao.WherePri(s.AttachmentDao.Columns.Id, req.Id).Scan(attachment)
  211. if err != nil {
  212. g.Log().Error(err)
  213. return myerrors.DbError("查询附件失败")
  214. }
  215. if attachment.Id <= 0 {
  216. return myerrors.TipsError("附件不存在")
  217. }
  218. _, err = s.AttachmentDao.WherePri(s.AttachmentDao.Columns.Id, req.Id).Data(g.Map{
  219. s.AttachmentDao.Columns.DeletedTime: gtime.Now(),
  220. s.AttachmentDao.Columns.UpdatedBy: s.GetCxtUserId(),
  221. s.AttachmentDao.Columns.UpdatedName: s.GetCxtUserName(),
  222. }).Update()
  223. if err != nil {
  224. g.Log().Error(err)
  225. return myerrors.DbError("删除附件失败")
  226. }
  227. return nil
  228. }
  229. // GetHistoryList 运维历史列表
  230. func (s *OperationService) GetHistoryList(req *opsdevmodel.OpsOperationEventHistorySearchReq) (total int, list []*opsdevmodel.OpsOperationEvent, err error) {
  231. db := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime)
  232. if !req.IncludeClosed {
  233. db = db.Where(s.Dao.Columns.EventStatus, opsdevmodel.EventStatusClosed)
  234. }
  235. if req.ScopeType == "my" {
  236. db = db.Where(s.Dao.Columns.OpsUserId, s.GetCxtUserId())
  237. }
  238. if req.EventTitle != "" {
  239. db = db.Where(s.Dao.Columns.EventTitle+" like ?", "%"+req.EventTitle+"%")
  240. }
  241. if req.CustName != "" {
  242. db = db.Where(s.Dao.Columns.CustName+" like ?", "%"+req.CustName+"%")
  243. }
  244. if req.FeedbackReporter != "" {
  245. db = db.Where(s.Dao.Columns.FeedbackReporter+" like ?", "%"+req.FeedbackReporter+"%")
  246. }
  247. if req.OpsUserName != "" {
  248. db = db.Where(s.Dao.Columns.OpsUserName+" like ?", "%"+req.OpsUserName+"%")
  249. }
  250. if req.BeginTime != "" {
  251. db = db.Where(s.Dao.Columns.FeedbackDate+" >= ?", req.BeginTime)
  252. }
  253. if req.EndTime != "" {
  254. db = db.Where(s.Dao.Columns.FeedbackDate+" <= ?", req.EndTime)
  255. }
  256. total, err = db.Count()
  257. if err != nil {
  258. g.Log().Error(err)
  259. return 0, nil, myerrors.DbError("查询运维历史数量失败")
  260. }
  261. pageNum, pageSize := req.GetPage()
  262. err = db.Page(pageNum, pageSize).Order(s.Dao.Columns.CompleteTime + " desc").Scan(&list)
  263. if err != nil {
  264. g.Log().Error(err)
  265. return 0, nil, myerrors.DbError("查询运维历史列表失败")
  266. }
  267. return total, list, nil
  268. }
  269. // Export 导出运维历史
  270. func (s *OperationService) Export(ctx context.Context, req *opsdevmodel.OpsOperationEventHistoryExport) (content *opsdevmodel.OpsOperationEventHistoryExportContent, err error) {
  271. db := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime)
  272. if !req.IncludeClosed {
  273. db = db.Where(s.Dao.Columns.EventStatus, opsdevmodel.EventStatusClosed)
  274. }
  275. if req.ScopeType == "my" {
  276. db = db.Where(s.Dao.Columns.OpsUserId, s.GetCxtUserId())
  277. }
  278. if req.EventTitle != "" {
  279. db = db.Where(s.Dao.Columns.EventTitle+" like ?", "%"+req.EventTitle+"%")
  280. }
  281. if req.CustName != "" {
  282. db = db.Where(s.Dao.Columns.CustName+" like ?", "%"+req.CustName+"%")
  283. }
  284. if req.FeedbackReporter != "" {
  285. db = db.Where(s.Dao.Columns.FeedbackReporter+" like ?", "%"+req.FeedbackReporter+"%")
  286. }
  287. if req.OpsUserName != "" {
  288. db = db.Where(s.Dao.Columns.OpsUserName+" like ?", "%"+req.OpsUserName+"%")
  289. }
  290. if req.BeginTime != "" {
  291. db = db.Where(s.Dao.Columns.FeedbackDate+" >= ?", req.BeginTime)
  292. }
  293. if req.EndTime != "" {
  294. db = db.Where(s.Dao.Columns.FeedbackDate+" <= ?", req.EndTime)
  295. }
  296. var list []*opsdevmodel.OpsOperationEvent
  297. err = db.Order(s.Dao.Columns.CompleteTime + " desc").Scan(&list)
  298. if err != nil {
  299. g.Log().Error(err)
  300. return nil, myerrors.DbError("查询运维历史失败")
  301. }
  302. // 获取所有事件ID
  303. var eventIds []int
  304. for _, item := range list {
  305. eventIds = append(eventIds, item.Id)
  306. }
  307. // 获取所有事件的处理记录
  308. var records []*opsdevmodel.OpsOperationEventRecord
  309. if len(eventIds) > 0 {
  310. err = s.RecordDao.WhereIn(s.RecordDao.Columns.EventId, eventIds).Order(s.RecordDao.Columns.HandleDate + " desc").Scan(&records)
  311. if err != nil {
  312. g.Log().Error(err)
  313. return nil, myerrors.DbError("查询处理记录失败")
  314. }
  315. }
  316. // 构建记录Map
  317. recordMap := make(map[int][]*opsdevmodel.OpsOperationEventRecord)
  318. for _, record := range records {
  319. recordMap[record.EventId] = append(recordMap[record.EventId], record)
  320. }
  321. // 构建状态Map
  322. statusMap := map[string]string{
  323. opsdevmodel.EventStatusPending: "待处理",
  324. opsdevmodel.EventStatusProcessing: "处理中(重点)",
  325. opsdevmodel.EventStatusProcessingNormal: "处理中(普通)",
  326. opsdevmodel.EventStatusTransfer: "转研发",
  327. opsdevmodel.EventStatusSuspended: "挂起",
  328. opsdevmodel.EventStatusClosed: "已关闭",
  329. }
  330. // 构建操作类型Map
  331. operateTypeMap := map[string]string{
  332. opsdevmodel.OperateTypeProcess: "处理",
  333. opsdevmodel.OperateTypeResume: "转处理",
  334. opsdevmodel.OperateTypeTransfer: "转研发",
  335. opsdevmodel.OperateTypeSuspend: "挂起",
  336. opsdevmodel.OperateTypeClose: "关闭",
  337. }
  338. // 构建处理结果Map
  339. handleResultMap := map[string]string{
  340. opsdevmodel.HandleResultResolved: "已解决",
  341. opsdevmodel.HandleResultPartially: "部分解决",
  342. opsdevmodel.HandleResultUnresolved: "未解决",
  343. }
  344. // 构建导出数据
  345. exportDataList := make([]map[string]interface{}, 0)
  346. for _, item := range list {
  347. // 构建处理过程
  348. processBuilder := ""
  349. if itemRecords, ok := recordMap[item.Id]; ok && len(itemRecords) > 0 {
  350. for i, record := range itemRecords {
  351. // 处理时间(仅年月)
  352. handleDate := ""
  353. if record.HandleDate != nil {
  354. handleDate = record.HandleDate.Format("Y-m-d H:i:s")
  355. }
  356. // 操作类型描述
  357. operateTypeStr := operateTypeMap[record.OperateType]
  358. if operateTypeStr == "" {
  359. operateTypeStr = record.OperateType
  360. }
  361. // 处理结果描述
  362. handleResultStr := handleResultMap[record.HandleResult]
  363. if handleResultStr == "" {
  364. handleResultStr = record.HandleResult
  365. }
  366. // 清理处理内容中的HTML标签
  367. cleanContent := cleanHtmlTags(record.HandleContent)
  368. recordStr := fmt.Sprintf("%s-%s-%s-%s-%s", handleDate, record.HandleUserName, operateTypeStr, handleResultStr, cleanContent)
  369. if i > 0 {
  370. processBuilder += "\n" + recordStr
  371. } else {
  372. processBuilder = recordStr
  373. }
  374. }
  375. }
  376. closedTime := ""
  377. if item.CompleteTime != nil {
  378. closedTime = item.CompleteTime.Format("Y-m-d")
  379. }
  380. feedbackDate := ""
  381. if item.FeedbackDate != nil {
  382. feedbackDate = item.FeedbackDate.Format("Y-m-d")
  383. }
  384. exportData := map[string]interface{}{
  385. "eventNo": item.EventNo,
  386. "eventTitle": item.EventTitle,
  387. "eventStatus": statusMap[item.EventStatus],
  388. "eventType": item.EventType,
  389. "custName": item.CustName,
  390. "opsUserName": item.OpsUserName,
  391. "feedbackDate": feedbackDate,
  392. "priorityLevel": item.PriorityLevel,
  393. "handleProcess": processBuilder,
  394. "closedTime": closedTime,
  395. }
  396. exportDataList = append(exportDataList, exportData)
  397. }
  398. // 调用公共导出方法
  399. exportContent := new(opsdevmodel.OpsOperationEventHistoryExportContent)
  400. contentBase64, err := service.CommonExportExcel(ctx, "运维历史", opsdevmodel.OpsOperationEventHistoryExportData{}, exportDataList)
  401. if err != nil {
  402. g.Log().Error(err)
  403. return nil, myerrors.DbError("导出运维历史失败")
  404. }
  405. exportContent.Content = contentBase64
  406. return exportContent, nil
  407. }
  408. func (s *OperationService) ExportNonClosed(ctx context.Context, req *opsdevmodel.OpsOperationEventExport) (content *opsdevmodel.OpsOperationEventHistoryExportContent, err error) {
  409. db := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).Where(s.Dao.Columns.EventStatus+" != ?", opsdevmodel.EventStatusClosed)
  410. var list []*opsdevmodel.OpsOperationEvent
  411. err = db.Order(s.Dao.Columns.FeedbackDate + " desc").Scan(&list)
  412. if err != nil {
  413. g.Log().Error(err)
  414. return nil, myerrors.DbError("查询运维事件失败")
  415. }
  416. var eventIds []int
  417. for _, item := range list {
  418. eventIds = append(eventIds, item.Id)
  419. }
  420. var records []*opsdevmodel.OpsOperationEventRecord
  421. if len(eventIds) > 0 {
  422. err = s.RecordDao.WhereIn(s.RecordDao.Columns.EventId, eventIds).Order(s.RecordDao.Columns.HandleDate + " desc").Scan(&records)
  423. if err != nil {
  424. g.Log().Error(err)
  425. return nil, myerrors.DbError("查询处理记录失败")
  426. }
  427. }
  428. recordMap := make(map[int][]*opsdevmodel.OpsOperationEventRecord)
  429. for _, record := range records {
  430. recordMap[record.EventId] = append(recordMap[record.EventId], record)
  431. }
  432. statusMap := map[string]string{
  433. opsdevmodel.EventStatusPending: "待处理",
  434. opsdevmodel.EventStatusProcessing: "处理中(重点)",
  435. opsdevmodel.EventStatusProcessingNormal: "处理中(普通)",
  436. opsdevmodel.EventStatusTransfer: "转研发",
  437. opsdevmodel.EventStatusSuspended: "挂起",
  438. opsdevmodel.EventStatusClosed: "已关闭",
  439. }
  440. operateTypeMap := map[string]string{
  441. opsdevmodel.OperateTypeProcess: "处理",
  442. opsdevmodel.OperateTypeResume: "转处理",
  443. opsdevmodel.OperateTypeTransfer: "转研发",
  444. opsdevmodel.OperateTypeSuspend: "挂起",
  445. opsdevmodel.OperateTypeClose: "关闭",
  446. }
  447. handleResultMap := map[string]string{
  448. opsdevmodel.HandleResultResolved: "已解决",
  449. opsdevmodel.HandleResultPartially: "部分解决",
  450. opsdevmodel.HandleResultUnresolved: "未解决",
  451. }
  452. exportDataList := make([]map[string]interface{}, 0)
  453. for _, item := range list {
  454. processBuilder := ""
  455. if itemRecords, ok := recordMap[item.Id]; ok && len(itemRecords) > 0 {
  456. for i, record := range itemRecords {
  457. handleDate := ""
  458. if record.HandleDate != nil {
  459. handleDate = record.HandleDate.Format("Y-m-d H:i:s")
  460. }
  461. operateTypeStr := operateTypeMap[record.OperateType]
  462. if operateTypeStr == "" {
  463. operateTypeStr = record.OperateType
  464. }
  465. handleResultStr := handleResultMap[record.HandleResult]
  466. if handleResultStr == "" {
  467. handleResultStr = record.HandleResult
  468. }
  469. cleanContent := cleanHtmlTags(record.HandleContent)
  470. recordStr := fmt.Sprintf("%s-%s-%s-%s-%s", handleDate, record.HandleUserName, operateTypeStr, handleResultStr, cleanContent)
  471. if i > 0 {
  472. processBuilder += "\n" + recordStr
  473. } else {
  474. processBuilder = recordStr
  475. }
  476. }
  477. }
  478. feedbackDate := ""
  479. if item.FeedbackDate != nil {
  480. feedbackDate = item.FeedbackDate.Format("Y-m-d")
  481. }
  482. exportData := map[string]interface{}{
  483. "eventNo": item.EventNo,
  484. "eventTitle": item.EventTitle,
  485. "eventStatus": statusMap[item.EventStatus],
  486. "eventType": item.EventType,
  487. "custName": item.CustName,
  488. "opsUserName": item.OpsUserName,
  489. "feedbackDate": feedbackDate,
  490. "priorityLevel": item.PriorityLevel,
  491. "handleProcess": processBuilder,
  492. }
  493. exportDataList = append(exportDataList, exportData)
  494. }
  495. exportContent := new(opsdevmodel.OpsOperationEventHistoryExportContent)
  496. contentBase64, err := service.CommonExportExcel(ctx, "运维事件", opsdevmodel.OpsOperationEventExportData{}, exportDataList)
  497. if err != nil {
  498. g.Log().Error(err)
  499. return nil, myerrors.DbError("导出运维事件失败")
  500. }
  501. exportContent.Content = contentBase64
  502. return exportContent, nil
  503. }
  504. func cleanHtmlTags(content string) string {
  505. if content == "" {
  506. return ""
  507. }
  508. imgPattern := regexp.MustCompile(`src=["']([^"']+)["']`)
  509. imgMatches := imgPattern.FindAllStringSubmatch(content, -1)
  510. seen := make(map[string]bool)
  511. var imgUrls []string
  512. for _, match := range imgMatches {
  513. if len(match) > 1 && !seen[match[1]] {
  514. seen[match[1]] = true
  515. imgUrls = append(imgUrls, match[1])
  516. }
  517. }
  518. htmlTagPattern := regexp.MustCompile(`<[^>]*>`)
  519. result := htmlTagPattern.ReplaceAllString(content, "")
  520. result = strings.ReplaceAll(result, "&nbsp;", " ")
  521. result = strings.ReplaceAll(result, "&amp;", "&")
  522. result = strings.ReplaceAll(result, "&lt;", "<")
  523. result = strings.ReplaceAll(result, "&gt;", ">")
  524. result = strings.ReplaceAll(result, "&quot;", "\"")
  525. result = strings.ReplaceAll(result, "&#39;", "'")
  526. result = strings.TrimSpace(result)
  527. if len(imgUrls) > 0 {
  528. result += "\n图片: "
  529. for i, url := range imgUrls {
  530. if i > 0 {
  531. result += ", "
  532. }
  533. result += url
  534. }
  535. }
  536. return result
  537. }
  538. // GetWorkHourList 获取工时登记列表
  539. func (s *OperationService) GetWorkHourList(req *opsdevmodel.OpsOperationWorkHourListReq) ([]*opsdevmodel.OpsOperationWorkHourListRsp, error) {
  540. var entities []*opsdevmodel.OpsOperationWorkHour
  541. err := s.WorkHourDao.FieldsEx(s.WorkHourDao.Columns.DeletedTime).
  542. Where(s.WorkHourDao.Columns.EventId, req.EventId).
  543. Order(s.WorkHourDao.Columns.CreatedTime + " desc").
  544. Scan(&entities)
  545. if err != nil {
  546. g.Log().Error(err)
  547. return nil, myerrors.DbError("查询工时登记列表失败")
  548. }
  549. list := make([]*opsdevmodel.OpsOperationWorkHourListRsp, 0, len(entities))
  550. for _, entity := range entities {
  551. workDate := ""
  552. if entity.WorkDate != nil {
  553. workDate = entity.WorkDate.Format("Y-m-d")
  554. }
  555. createdTime := ""
  556. if entity.CreatedTime != nil {
  557. createdTime = entity.CreatedTime.Format("Y-m-d H:i:s")
  558. }
  559. list = append(list, &opsdevmodel.OpsOperationWorkHourListRsp{
  560. Id: entity.Id,
  561. WorkDate: workDate,
  562. WorkHour: entity.WorkHour,
  563. Remark: entity.Remark,
  564. CreatedName: entity.CreatedName,
  565. CreatedTime: createdTime,
  566. })
  567. }
  568. return list, nil
  569. }
  570. // generateEventNo 生成运维事件编号
  571. func (s *OperationService) generateEventNo() string {
  572. now := gtime.Now()
  573. prefix := "OPS" + now.Format("Ymd")
  574. seqVal, err := s.Dao.DB.GetValue("SELECT next_day_reset_val('event_no_seq')")
  575. if err == nil {
  576. return prefix + fmt.Sprintf("%04d", seqVal.Int())
  577. }
  578. var maxNoResult struct {
  579. EventNo string
  580. }
  581. err = s.Dao.Where("event_no like ?", prefix+"%").
  582. Order("event_no desc").
  583. Fields("event_no").
  584. Scan(&maxNoResult)
  585. if err != nil || maxNoResult.EventNo == "" {
  586. return prefix + "0001"
  587. }
  588. maxNoStr := maxNoResult.EventNo
  589. if len(maxNoStr) >= len(prefix)+4 {
  590. seq := maxNoStr[len(prefix):]
  591. seqNum := gconv.Int(seq)
  592. seqNum++
  593. return prefix + fmt.Sprintf("%04d", seqNum)
  594. }
  595. return prefix + "0001"
  596. }
  597. // Process 处理运维事件(开始/转处理/转研发/挂起/关闭)
  598. func (s *OperationService) Process(ctx context.Context, req *opsdevmodel.OpsOperationEventProcessReq) error {
  599. event := new(opsdevmodel.OpsOperationEvent)
  600. err := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).WherePri(s.Dao.Columns.Id, int(req.Id)).Scan(event)
  601. if err != nil {
  602. g.Log().Error(err)
  603. return myerrors.DbError("查询运维事件失败")
  604. }
  605. if event.Id <= 0 {
  606. return myerrors.TipsError("运维事件不存在")
  607. }
  608. opsUserId := s.GetCxtUserId()
  609. opsUserName := s.GetCxtUserName()
  610. switch req.OperateType {
  611. case opsdevmodel.OperateTypeClose:
  612. return s.processClose(req, event, opsUserId, opsUserName)
  613. default:
  614. return s.processNormal(req, event, opsUserId, opsUserName)
  615. }
  616. }
  617. // processClose 关单处理(含工时调整)
  618. func (s *OperationService) processClose(req *opsdevmodel.OpsOperationEventProcessReq, event *opsdevmodel.OpsOperationEvent, opsUserId int, opsUserName string) error {
  619. return s.Dao.Transaction(s.Ctx, func(ctx context.Context, tx *gdb.TX) error {
  620. handleDate := gtime.Now()
  621. recordData := g.Map{
  622. s.RecordDao.Columns.EventId: req.Id,
  623. s.RecordDao.Columns.HandleUserId: opsUserId,
  624. s.RecordDao.Columns.HandleUserName: opsUserName,
  625. s.RecordDao.Columns.HandleContent: req.HandleContent,
  626. s.RecordDao.Columns.HandleResult: req.HandleResult,
  627. s.RecordDao.Columns.HandleDate: handleDate,
  628. s.RecordDao.Columns.OperateType: req.OperateType,
  629. }
  630. service.SetCreatedInfo(recordData, opsUserId, opsUserName)
  631. _, err := s.RecordDao.TX(tx).Data(recordData).Insert()
  632. if err != nil {
  633. g.Log().Error(err)
  634. return myerrors.DbError("创建处理记录失败")
  635. }
  636. eventUpdateData := g.Map{
  637. s.Dao.Columns.EventStatus: opsdevmodel.EventStatusClosed,
  638. s.Dao.Columns.CompleteTime: gtime.Now(),
  639. s.Dao.Columns.CompleteDesc: req.HandleContent,
  640. s.Dao.Columns.EventResult: req.HandleResult,
  641. }
  642. service.SetUpdatedInfo(eventUpdateData, opsUserId, opsUserName)
  643. if req.AdjustWorkHour > 0 {
  644. currentTotal := event.TotalWorkHour
  645. if req.AdjustWorkHour < currentTotal {
  646. return myerrors.ValidError("工时只能增加不能减少")
  647. }
  648. if req.AdjustWorkHour > currentTotal {
  649. delta := req.AdjustWorkHour - currentTotal
  650. workHourData := g.Map{
  651. s.WorkHourDao.Columns.EventId: int(req.Id),
  652. s.WorkHourDao.Columns.OpsUserId: opsUserId,
  653. s.WorkHourDao.Columns.OpsUserName: opsUserName,
  654. s.WorkHourDao.Columns.WorkDate: gtime.Now(),
  655. s.WorkHourDao.Columns.WorkHour: delta,
  656. s.WorkHourDao.Columns.Remark: "关单调整",
  657. }
  658. service.SetCreatedInfo(workHourData, opsUserId, opsUserName)
  659. _, err := s.WorkHourDao.TX(tx).Data(workHourData).Insert()
  660. if err != nil {
  661. g.Log().Error(err)
  662. return myerrors.DbError("创建工时调整记录失败")
  663. }
  664. eventUpdateData[s.Dao.Columns.TotalWorkHour] = req.AdjustWorkHour
  665. }
  666. }
  667. _, err = s.Dao.TX(tx).FieldsEx(service.UpdateFieldEx...).Data(eventUpdateData).WherePri(s.Dao.Columns.Id, req.Id).Update()
  668. if err != nil {
  669. g.Log().Error(err)
  670. return myerrors.DbError("关闭事件失败")
  671. }
  672. return nil
  673. })
  674. }
  675. // getDevTaskType 根据运维事件类型获取对应的研发任务类型,未匹配时兜底返回功能开发(20)
  676. func (s *OperationService) getDevTaskType(eventType string) string {
  677. if taskType, ok := eventTypeToDevTaskType[eventType]; ok {
  678. return taskType
  679. }
  680. return opsdevmodel.TaskTypeFeatureDev
  681. }
  682. // createDevTaskFromEvent 根据运维事件自动创建研发任务(在processNormal事务完成后调用)
  683. func (s *OperationService) createDevTaskFromEvent(event *opsdevmodel.OpsOperationEvent) {
  684. var attachments []*opsdevmodel.OpsOperationEventAttachment
  685. err := s.AttachmentDao.FieldsEx(s.AttachmentDao.Columns.DeletedTime).
  686. Where(s.AttachmentDao.Columns.EventId, event.Id).
  687. Scan(&attachments)
  688. if err != nil {
  689. g.Log().Errorf("createDevTaskFromEvent: query attachments failed, eventId=%d, err=%v", event.Id, err)
  690. }
  691. taskAttachments := make([]opsdevmodel.Attachment, 0, len(attachments))
  692. for _, att := range attachments {
  693. taskAttachments = append(taskAttachments, opsdevmodel.Attachment{
  694. FileName: att.FileName,
  695. FileUrl: att.FileUrl,
  696. FileType: att.FileType,
  697. })
  698. }
  699. taskType := s.getDevTaskType(event.EventType)
  700. projectId := 0
  701. if event.ContractId > 0 {
  702. var project opsdevmodel.OpsDeliveryProject
  703. err := s.DeliveryProjectDao.FieldsEx(s.DeliveryProjectDao.Columns.DeletedTime).
  704. Where(s.DeliveryProjectDao.Columns.ContractId, event.ContractId).
  705. OrderDesc(s.DeliveryProjectDao.Columns.Id).
  706. Limit(1).
  707. Scan(&project)
  708. if err != nil {
  709. g.Log().Warningf("createDevTaskFromEvent: query project by contractId=%d failed, err=%v", event.ContractId, err)
  710. } else if project.Id > 0 {
  711. projectId = project.Id
  712. } else {
  713. g.Log().Warningf("createDevTaskFromEvent: no project found for contractId=%d", event.ContractId)
  714. }
  715. }
  716. taskReq := &opsdevmodel.OpsEventTaskAddReq{
  717. ProjectId: projectId,
  718. TaskTitle: event.EventTitle,
  719. TaskDesc: event.EventDesc,
  720. TaskType: taskType,
  721. Priority: event.PriorityLevel,
  722. EventId: event.Id,
  723. EventType: opsdevmodel.EventTypeOps,
  724. Attachments: taskAttachments,
  725. }
  726. taskSvc, err := NewOpsEventTaskService(s.Ctx)
  727. if err != nil {
  728. g.Log().Errorf("createDevTaskFromEvent: init task service failed, eventId=%d, err=%v", event.Id, err)
  729. return
  730. }
  731. if err := taskSvc.Create(taskReq); err != nil {
  732. g.Log().Errorf("createDevTaskFromEvent: create task failed, eventId=%d, eventType=%s, taskType=%s, err=%v",
  733. event.Id, event.EventType, taskType, err)
  734. }
  735. }
  736. // processNormal 非关单处理(开始/转处理/转研发/挂起)
  737. func (s *OperationService) processNormal(req *opsdevmodel.OpsOperationEventProcessReq, event *opsdevmodel.OpsOperationEvent, opsUserId int, opsUserName string) error {
  738. handleDate := gtime.Now()
  739. recordData := g.Map{
  740. s.RecordDao.Columns.EventId: req.Id,
  741. s.RecordDao.Columns.HandleUserId: opsUserId,
  742. s.RecordDao.Columns.HandleUserName: opsUserName,
  743. s.RecordDao.Columns.HandleContent: req.HandleContent,
  744. s.RecordDao.Columns.HandleResult: req.HandleResult,
  745. s.RecordDao.Columns.HandleDate: handleDate,
  746. s.RecordDao.Columns.OperateType: req.OperateType,
  747. }
  748. service.SetCreatedInfo(recordData, opsUserId, opsUserName)
  749. newStatus := ""
  750. switch req.OperateType {
  751. case opsdevmodel.OperateTypeProcess:
  752. newStatus = opsdevmodel.EventStatusProcessing
  753. case opsdevmodel.OperateTypeResume:
  754. newStatus = opsdevmodel.EventStatusProcessing
  755. case opsdevmodel.OperateTypeTransfer:
  756. newStatus = opsdevmodel.EventStatusTransfer
  757. case opsdevmodel.OperateTypeSuspend:
  758. newStatus = opsdevmodel.EventStatusSuspended
  759. }
  760. err := s.Dao.Transaction(s.Ctx, func(ctx context.Context, tx *gdb.TX) error {
  761. _, err := s.RecordDao.TX(tx).Data(recordData).Insert()
  762. if err != nil {
  763. g.Log().Error(err)
  764. return myerrors.DbError("创建处理记录失败")
  765. }
  766. if newStatus != "" {
  767. updateData := g.Map{
  768. s.Dao.Columns.EventStatus: newStatus,
  769. }
  770. service.SetUpdatedInfo(updateData, opsUserId, opsUserName)
  771. _, err := s.Dao.TX(tx).FieldsEx(service.UpdateFieldEx...).Data(updateData).WherePri(s.Dao.Columns.Id, req.Id).Update()
  772. if err != nil {
  773. g.Log().Error(err)
  774. return myerrors.DbError("更新事件状态失败")
  775. }
  776. }
  777. return nil
  778. })
  779. if err != nil {
  780. return err
  781. }
  782. if req.OperateType == opsdevmodel.OperateTypeTransfer {
  783. s.createDevTaskFromEvent(event)
  784. }
  785. return nil
  786. }
  787. // UpdateById 更新运维事件
  788. func (s *OperationService) UpdateById(req *opsdevmodel.UpdateOpsOperationEventReq) error {
  789. if req.Id <= 0 {
  790. return myerrors.ValidError("参数有误!")
  791. }
  792. event := new(opsdevmodel.OpsOperationEvent)
  793. err := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).WherePri(s.Dao.Columns.Id, req.Id).Scan(event)
  794. if err != nil {
  795. g.Log().Error(err)
  796. return myerrors.DbError("查询运维事件失败")
  797. }
  798. if event.Id <= 0 {
  799. return myerrors.TipsError("运维事件不存在")
  800. }
  801. data := g.Map{}
  802. if req.EventTitle != "" {
  803. data[s.Dao.Columns.EventTitle] = req.EventTitle
  804. }
  805. if req.EventDesc != "" {
  806. data[s.Dao.Columns.EventDesc] = req.EventDesc
  807. }
  808. if req.EventType != "" {
  809. data[s.Dao.Columns.EventType] = req.EventType
  810. }
  811. if req.EventStatus != "" {
  812. data[s.Dao.Columns.EventStatus] = req.EventStatus
  813. }
  814. if req.ContractId > 0 {
  815. data[s.Dao.Columns.ContractId] = req.ContractId
  816. }
  817. if req.ContractName != "" {
  818. data[s.Dao.Columns.ContractName] = req.ContractName
  819. }
  820. if req.CustId > 0 {
  821. data[s.Dao.Columns.CustId] = req.CustId
  822. }
  823. if req.CustName != "" {
  824. data[s.Dao.Columns.CustName] = req.CustName
  825. }
  826. if req.ProductLine != "" {
  827. data[s.Dao.Columns.ProductLine] = req.ProductLine
  828. }
  829. if req.IsBig != "" {
  830. data[s.Dao.Columns.IsBig] = req.IsBig
  831. }
  832. if req.IsOps != "" {
  833. data[s.Dao.Columns.IsOps] = req.IsOps
  834. }
  835. if req.PriorityLevel != "" {
  836. data[s.Dao.Columns.PriorityLevel] = req.PriorityLevel
  837. }
  838. if req.FeedbackSource != "" {
  839. data[s.Dao.Columns.FeedbackSource] = req.FeedbackSource
  840. }
  841. if req.FeedbackReporter != "" {
  842. data[s.Dao.Columns.FeedbackReporter] = req.FeedbackReporter
  843. }
  844. if req.FeedbackDate != "" {
  845. data[s.Dao.Columns.FeedbackDate] = req.FeedbackDate
  846. }
  847. if req.OpsUserId > 0 {
  848. data[s.Dao.Columns.OpsUserId] = req.OpsUserId
  849. }
  850. if req.OpsUserName != "" {
  851. data[s.Dao.Columns.OpsUserName] = req.OpsUserName
  852. }
  853. if req.Remark != "" {
  854. data[s.Dao.Columns.Remark] = req.Remark
  855. }
  856. service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  857. _, err = s.Dao.FieldsEx(service.UpdateFieldEx...).Data(data).WherePri(s.Dao.Columns.Id, req.Id).Update()
  858. if err != nil {
  859. g.Log().Error(err)
  860. return myerrors.DbError("更新运维事件失败")
  861. }
  862. return nil
  863. }
  864. // DeleteByIds 根据ID批量删除运维事件(软删除)
  865. func (s *OperationService) DeleteByIds(req *comm_def.IdsReq) error {
  866. if len(req.Ids) == 0 {
  867. return myerrors.ValidError("参数有误!")
  868. }
  869. ids := make([]int, 0, len(req.Ids))
  870. for _, id := range req.Ids {
  871. ids = append(ids, int(id))
  872. }
  873. return s.Dao.Transaction(s.Ctx, func(ctx context.Context, tx *gdb.TX) error {
  874. // 1. 软删除事件
  875. _, err := s.Dao.TX(tx).Data(g.Map{
  876. s.Dao.Columns.DeletedTime: gtime.Now(),
  877. }).WhereIn(s.Dao.Columns.Id, ids).Update()
  878. if err != nil {
  879. g.Log().Error(err)
  880. return myerrors.DbError("删除运维事件失败")
  881. }
  882. // 2. 软删除关联的处理记录
  883. _, err = s.RecordDao.TX(tx).Data(g.Map{
  884. s.RecordDao.Columns.DeletedTime: gtime.Now(),
  885. }).WhereIn(s.RecordDao.Columns.EventId, ids).Update()
  886. if err != nil {
  887. g.Log().Error(err)
  888. return myerrors.DbError("删除处理记录失败")
  889. }
  890. // 3. 软删除关联的附件
  891. _, err = s.AttachmentDao.TX(tx).Data(g.Map{
  892. s.AttachmentDao.Columns.DeletedTime: gtime.Now(),
  893. }).WhereIn(s.AttachmentDao.Columns.EventId, ids).Update()
  894. if err != nil {
  895. g.Log().Error(err)
  896. return myerrors.DbError("删除附件失败")
  897. }
  898. return nil
  899. })
  900. }
  901. // GetRecords 获取事件处理记录列表(分页,含附件)
  902. func (s *OperationService) GetRecords(req *opsdevmodel.OpsOperationEventRecordSearchReq) (total int, list []*opsdevmodel.OpsOperationEventRecordWithAttachments, err error) {
  903. if req.EventId <= 0 {
  904. return 0, nil, myerrors.ValidError("事件ID不能为空")
  905. }
  906. db := s.RecordDao.FieldsEx(s.RecordDao.Columns.DeletedTime).Where(s.RecordDao.Columns.EventId, req.EventId)
  907. total, err = db.Count()
  908. if err != nil {
  909. g.Log().Error(err)
  910. return 0, nil, myerrors.DbError("查询处理记录数量失败")
  911. }
  912. pageNum, pageSize := req.GetPage()
  913. var records []*opsdevmodel.OpsOperationEventRecord
  914. err = db.Page(pageNum, pageSize).Order(s.RecordDao.Columns.CreatedTime + " desc").Scan(&records)
  915. if err != nil {
  916. g.Log().Error(err)
  917. return 0, nil, myerrors.DbError("查询处理记录列表失败")
  918. }
  919. result := make([]*opsdevmodel.OpsOperationEventRecordWithAttachments, 0, len(records))
  920. for _, record := range records {
  921. recordRsp := &opsdevmodel.OpsOperationEventRecordWithAttachments{
  922. OpsOperationEventRecord: *record,
  923. Attachments: []*opsdevmodel.OpsOperationEventAttachment{},
  924. }
  925. if record.Id > 0 {
  926. var attachments []*opsdevmodel.OpsOperationEventAttachment
  927. err := s.AttachmentDao.FieldsEx(s.AttachmentDao.Columns.DeletedTime).
  928. Where(s.AttachmentDao.Columns.EventRecordId, record.Id).
  929. Scan(&attachments)
  930. if err != nil {
  931. g.Log().Error(err)
  932. } else {
  933. recordRsp.Attachments = attachments
  934. }
  935. }
  936. result = append(result, recordRsp)
  937. }
  938. return total, result, nil
  939. }
  940. // UploadAttachment 上传附件
  941. func (s *OperationService) UploadAttachment(req *opsdevmodel.OpsOperationEventAttachmentReq) (*opsdevmodel.OpsOperationEventAttachment, error) {
  942. if req.EventId <= 0 {
  943. return nil, myerrors.ValidError("事件ID不能为空")
  944. }
  945. event := new(opsdevmodel.OpsOperationEvent)
  946. err := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).WherePri(s.Dao.Columns.Id, req.EventId).Scan(event)
  947. if err != nil {
  948. g.Log().Error(err)
  949. return nil, myerrors.DbError("查询运维事件失败")
  950. }
  951. if event.Id <= 0 {
  952. return nil, myerrors.TipsError("运维事件不存在")
  953. }
  954. data := g.Map{
  955. s.AttachmentDao.Columns.EventId: req.EventId,
  956. s.AttachmentDao.Columns.EventRecordId: req.EventRecordId,
  957. s.AttachmentDao.Columns.FileName: req.FileName,
  958. s.AttachmentDao.Columns.FileUrl: req.FileUrl,
  959. s.AttachmentDao.Columns.FileType: req.FileType,
  960. s.AttachmentDao.Columns.Remark: req.Remark,
  961. }
  962. service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  963. result, err := s.AttachmentDao.Data(data).Insert()
  964. if err != nil {
  965. g.Log().Error(err)
  966. return nil, myerrors.DbError("上传附件失败")
  967. }
  968. id, err := result.LastInsertId()
  969. if err != nil {
  970. g.Log().Error(err)
  971. return nil, myerrors.DbError("获取附件ID失败")
  972. }
  973. attachment := new(opsdevmodel.OpsOperationEventAttachment)
  974. err = s.AttachmentDao.WherePri(s.AttachmentDao.Columns.Id, id).Scan(attachment)
  975. if err != nil {
  976. g.Log().Error(err)
  977. return nil, myerrors.DbError("查询附件失败")
  978. }
  979. return attachment, nil
  980. }
  981. // GetAttachments 根据事件ID获取附件列表
  982. func (s *OperationService) GetAttachments(eventId int) ([]*opsdevmodel.OpsOperationEventAttachment, error) {
  983. if eventId <= 0 {
  984. return nil, myerrors.ValidError("事件ID不能为空")
  985. }
  986. var attachments []*opsdevmodel.OpsOperationEventAttachment
  987. err := s.AttachmentDao.FieldsEx(s.AttachmentDao.Columns.DeletedTime).
  988. Where(s.AttachmentDao.Columns.EventId, eventId).
  989. Order(s.AttachmentDao.Columns.CreatedTime + " desc").
  990. Scan(&attachments)
  991. if err != nil {
  992. g.Log().Error(err)
  993. return nil, myerrors.DbError("查询附件列表失败")
  994. }
  995. return attachments, nil
  996. }
  997. // GetStats 获取运维事件统计数据
  998. func (s *OperationService) GetStats() (g.Map, error) {
  999. stats := g.Map{}
  1000. // 按状态统计
  1001. db := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime)
  1002. statusList, err := db.GroupBy(s.Dao.Columns.EventStatus).Fields(s.Dao.Columns.EventStatus+", count(*) as count").All()
  1003. if err != nil {
  1004. g.Log().Error(err)
  1005. return nil, myerrors.DbError("统计事件状态失败")
  1006. }
  1007. statusCount := g.Map{}
  1008. for _, row := range statusList {
  1009. statusCount[gconv.String(row["event_status"])] = row["count"]
  1010. }
  1011. stats["statusCount"] = statusCount
  1012. // 按优先级统计
  1013. priorityList, err := db.GroupBy(s.Dao.Columns.PriorityLevel).Fields(s.Dao.Columns.PriorityLevel+", count(*) as count").All()
  1014. if err != nil {
  1015. g.Log().Error(err)
  1016. return nil, myerrors.DbError("统计事件优先级失败")
  1017. }
  1018. priorityCount := g.Map{}
  1019. for _, row := range priorityList {
  1020. priorityCount[gconv.String(row["priority_level"])] = row["count"]
  1021. }
  1022. stats["priorityCount"] = priorityCount
  1023. // 按类型统计
  1024. typeList, err := db.GroupBy(s.Dao.Columns.EventType).Fields(s.Dao.Columns.EventType+", count(*) as count").All()
  1025. if err != nil {
  1026. g.Log().Error(err)
  1027. return nil, myerrors.DbError("统计事件类型失败")
  1028. }
  1029. typeCount := g.Map{}
  1030. for _, row := range typeList {
  1031. typeCount[gconv.String(row["event_type"])] = row["count"]
  1032. }
  1033. stats["typeCount"] = typeCount
  1034. return stats, nil
  1035. }
  1036. // GetKanbanData 获取看板数据
  1037. func (s *OperationService) GetKanbanData(req *opsdevmodel.OpsOperationEventKanbanSearchReq) (g.Map, error) {
  1038. // 查询所有非关闭状态的事件
  1039. db := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).Where(s.Dao.Columns.EventStatus+" != ?", opsdevmodel.EventStatusClosed)
  1040. if req.KeyWords != "" {
  1041. switch req.SearchType {
  1042. case "eventNo":
  1043. db = db.Where(s.Dao.Columns.EventNo, req.KeyWords)
  1044. case "title":
  1045. db = db.Where(s.Dao.Columns.EventTitle+" like ?", "%"+req.KeyWords+"%")
  1046. case "custName":
  1047. db = db.Where(s.Dao.Columns.CustName+" like ?", "%"+req.KeyWords+"%")
  1048. case "feedbackReporter":
  1049. db = db.Where(s.Dao.Columns.FeedbackReporter+" like ?", "%"+req.KeyWords+"%")
  1050. default:
  1051. db = db.Where(fmt.Sprintf("(%s like ? or %s like ? or %s like ? or %s = ?)",
  1052. s.Dao.Columns.EventTitle, s.Dao.Columns.CustName, s.Dao.Columns.FeedbackReporter, s.Dao.Columns.EventNo),
  1053. "%"+req.KeyWords+"%", "%"+req.KeyWords+"%", "%"+req.KeyWords+"%", req.KeyWords)
  1054. }
  1055. }
  1056. if req.EventType != "" {
  1057. db = db.Where(s.Dao.Columns.EventType, req.EventType)
  1058. }
  1059. if req.PriorityLevel != "" {
  1060. db = db.Where(s.Dao.Columns.PriorityLevel, req.PriorityLevel)
  1061. }
  1062. // 待处理可查看全部,处理中/转研发/挂起仅查看当前登录人的
  1063. userId := s.GetCxtUserId()
  1064. if userId > 0 {
  1065. db = db.Where(fmt.Sprintf("(%s = ? OR %s = ?)", s.Dao.Columns.EventStatus, s.Dao.Columns.OpsUserId), opsdevmodel.EventStatusPending, userId)
  1066. }
  1067. // 排序
  1068. switch req.SortBy {
  1069. case "feedbackDateAsc":
  1070. db = db.Order(s.Dao.Columns.FeedbackDate + " asc")
  1071. case "custName":
  1072. db = db.Order(s.Dao.Columns.CustName + " asc")
  1073. default:
  1074. db = db.Order(s.Dao.Columns.FeedbackDate + " desc")
  1075. }
  1076. var list []*opsdevmodel.OpsOperationEvent
  1077. err := db.Scan(&list)
  1078. if err != nil {
  1079. g.Log().Error(err)
  1080. return nil, myerrors.DbError("查询看板数据失败")
  1081. }
  1082. // 按状态分组(处理中合并 20 + 30)
  1083. groups := map[string][]*opsdevmodel.OpsOperationEvent{
  1084. opsdevmodel.EventStatusPending: make([]*opsdevmodel.OpsOperationEvent, 0), // 待处理
  1085. opsdevmodel.EventStatusProcessing: make([]*opsdevmodel.OpsOperationEvent, 0), // 处理中
  1086. opsdevmodel.EventStatusTransfer: make([]*opsdevmodel.OpsOperationEvent, 0), // 转研发
  1087. opsdevmodel.EventStatusSuspended: make([]*opsdevmodel.OpsOperationEvent, 0), // 挂起
  1088. }
  1089. for _, item := range list {
  1090. key := item.EventStatus
  1091. if key == opsdevmodel.EventStatusProcessingNormal {
  1092. key = opsdevmodel.EventStatusProcessing
  1093. }
  1094. groups[key] = append(groups[key], item)
  1095. }
  1096. kanbanData := g.Map{}
  1097. for key, items := range groups {
  1098. kanbanData[key] = g.Map{
  1099. "list": items,
  1100. "count": len(items),
  1101. }
  1102. }
  1103. return kanbanData, nil
  1104. }
  1105. // AssignOpsUser 分配运维人员
  1106. func (s *OperationService) AssignOpsUser(req *opsdevmodel.AssignOpsUserReq) error {
  1107. if req.Id <= 0 {
  1108. return myerrors.ValidError("事件ID不能为空")
  1109. }
  1110. event := new(opsdevmodel.OpsOperationEvent)
  1111. err := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).WherePri(s.Dao.Columns.Id, req.Id).Scan(event)
  1112. if err != nil {
  1113. g.Log().Error(err)
  1114. return myerrors.DbError("查询运维事件失败")
  1115. }
  1116. if event.Id <= 0 {
  1117. return myerrors.TipsError("运维事件不存在")
  1118. }
  1119. data := g.Map{
  1120. s.Dao.Columns.OpsUserId: req.OpsUserId,
  1121. s.Dao.Columns.OpsUserName: req.OpsUserName,
  1122. s.Dao.Columns.AssignTime: gtime.Now(),
  1123. }
  1124. service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  1125. _, err = s.Dao.FieldsEx(service.UpdateFieldEx...).Data(data).WherePri(s.Dao.Columns.Id, req.Id).Update()
  1126. if err != nil {
  1127. g.Log().Error(err)
  1128. return myerrors.DbError("分配运维人员失败")
  1129. }
  1130. return nil
  1131. }
  1132. // AddRecord 添加处理记录(含附件,事务控制)
  1133. func (s *OperationService) AddRecord(req *opsdevmodel.AddRecordReq) error {
  1134. if req.EventId <= 0 {
  1135. return myerrors.ValidError("事件ID不能为空")
  1136. }
  1137. event := new(opsdevmodel.OpsOperationEvent)
  1138. err := s.Dao.FieldsEx(s.Dao.Columns.DeletedTime).WherePri(s.Dao.Columns.Id, req.EventId).Scan(event)
  1139. if err != nil {
  1140. g.Log().Error(err)
  1141. return myerrors.DbError("查询运维事件失败")
  1142. }
  1143. if event.Id <= 0 {
  1144. return myerrors.TipsError("运维事件不存在")
  1145. }
  1146. opsUserId := s.GetCxtUserId()
  1147. opsUserName := s.GetCxtUserName()
  1148. recordData := g.Map{
  1149. s.RecordDao.Columns.EventId: req.EventId,
  1150. s.RecordDao.Columns.HandleUserId: opsUserId,
  1151. s.RecordDao.Columns.HandleUserName: opsUserName,
  1152. s.RecordDao.Columns.HandleContent: req.HandleContent,
  1153. s.RecordDao.Columns.HandleResult: req.HandleResult,
  1154. s.RecordDao.Columns.HandleDate: gtime.Now(),
  1155. }
  1156. service.SetCreatedInfo(recordData, opsUserId, opsUserName)
  1157. return s.RecordDao.Transaction(s.Ctx, func(ctx context.Context, tx *gdb.TX) error {
  1158. result, err := s.RecordDao.TX(tx).Data(recordData).Insert()
  1159. if err != nil {
  1160. g.Log().Error(err)
  1161. return myerrors.DbError("添加处理记录失败")
  1162. }
  1163. recordId, err := result.LastInsertId()
  1164. if err != nil {
  1165. g.Log().Error(err)
  1166. return myerrors.DbError("获取记录ID失败")
  1167. }
  1168. // 如果有附件,一并保存
  1169. for _, att := range req.Attachments {
  1170. attData := g.Map{
  1171. s.AttachmentDao.Columns.EventId: req.EventId,
  1172. s.AttachmentDao.Columns.EventRecordId: recordId,
  1173. s.AttachmentDao.Columns.FileName: att.FileName,
  1174. s.AttachmentDao.Columns.FileUrl: att.FileUrl,
  1175. s.AttachmentDao.Columns.FileType: att.FileType,
  1176. }
  1177. service.SetCreatedInfo(attData, opsUserId, opsUserName)
  1178. _, err := s.AttachmentDao.TX(tx).Data(attData).Insert()
  1179. if err != nil {
  1180. g.Log().Error(err)
  1181. return myerrors.DbError("保存附件失败")
  1182. }
  1183. }
  1184. return nil
  1185. })
  1186. }