operation.go 42 KB

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