delivery_project_event.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. package opsdev
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "dashoo.cn/opms_libary/myerrors"
  7. eventdao "dashoo.cn/opms_parent/app/dao/opsdev"
  8. eventmodel "dashoo.cn/opms_parent/app/model/opsdev"
  9. "dashoo.cn/opms_parent/app/service"
  10. "github.com/gogf/gf/database/gdb"
  11. "github.com/gogf/gf/frame/g"
  12. "github.com/gogf/gf/os/gtime"
  13. "github.com/gogf/gf/util/gconv"
  14. )
  15. // eventTypeToAutoTaskType 交付事件类型→自动创建研发任务类型的映射
  16. var eventTypeToAutoTaskType = map[string]string{
  17. "31": eventmodel.TaskTypeReqReview, // 需求评审 → 需求评审
  18. "32": eventmodel.TaskTypeFeatureDev, // 功能调整 → 功能开发
  19. "33": eventmodel.TaskTypeFeatureDev, // 二开需求 → 功能开发
  20. "35": eventmodel.TaskTypeFeatureDev, // 系统缺陷 → 功能开发
  21. "38": eventmodel.TaskTypeSystemReleaseEvt, // 系统发版 → 系统发版(事件)
  22. "40": eventmodel.TaskTypeSystemReleaseEvt, // 硬件发货 → 系统发版(事件)
  23. "41": eventmodel.TaskTypeSystemReleaseEvt, // 硬件安装 → 系统发版(事件)
  24. }
  25. // DeliveryProjectEventService 交付项目事件业务逻辑实现类
  26. type DeliveryProjectEventService struct {
  27. *service.ContextService
  28. EventDao *eventdao.OpsDeliveryProjectEventDao
  29. RecordDao *eventdao.OpsDeliveryProjectEventRecordDao
  30. AttachmentDao *eventdao.OpsDeliveryProjectEventAttachmentDao
  31. ProjectDao *eventdao.OpsDeliveryProjectDao
  32. TaskDao *eventdao.OpsEventTaskDao
  33. TaskRecordDao *eventdao.OpsEventTaskRecordDao
  34. }
  35. // NewDeliveryProjectEventService 初始化service
  36. func NewDeliveryProjectEventService(ctx context.Context) (svc *DeliveryProjectEventService, err error) {
  37. svc = new(DeliveryProjectEventService)
  38. if svc.ContextService, err = svc.Init(ctx); err != nil {
  39. return nil, err
  40. }
  41. svc.EventDao = eventdao.NewOpsDeliveryProjectEventDao(svc.Tenant)
  42. svc.RecordDao = eventdao.NewOpsDeliveryProjectEventRecordDao(svc.Tenant)
  43. svc.AttachmentDao = eventdao.NewOpsDeliveryProjectEventAttachmentDao(svc.Tenant)
  44. svc.ProjectDao = eventdao.NewOpsDeliveryProjectDao(svc.Tenant)
  45. svc.TaskDao = eventdao.NewOpsEventTaskDao(svc.Tenant)
  46. svc.TaskRecordDao = eventdao.NewOpsEventTaskRecordDao(svc.Tenant)
  47. return svc, nil
  48. }
  49. // GetList 分页查询事件列表
  50. func (s *DeliveryProjectEventService) GetList(req *eventmodel.OpsDeliveryProjectEventSearchReq) (total int, list []*eventmodel.OpsDeliveryProjectEventRsp, err error) {
  51. db := s.EventDao.FieldsEx(s.EventDao.Columns.DeletedTime)
  52. // 项目ID筛选
  53. if req.ProjectId > 0 {
  54. db = db.Where(s.EventDao.Columns.ProjectId, req.ProjectId)
  55. }
  56. // 事件标题模糊查询
  57. if req.DeliveryEventTitle != "" {
  58. db = db.Where(s.EventDao.Columns.DeliveryEventTitle+" like ?", "%"+req.DeliveryEventTitle+"%")
  59. }
  60. // 事件描述模糊查询
  61. if req.DeliveryEventDesc != "" {
  62. db = db.Where(s.EventDao.Columns.DeliveryEventDesc+" like ?", "%"+req.DeliveryEventDesc+"%")
  63. }
  64. // 事件类型筛选(多选)
  65. if len(req.DeliveryEventType) > 0 {
  66. db = db.Where(s.EventDao.Columns.DeliveryEventType+" in (?)", req.DeliveryEventType)
  67. }
  68. // 事件状态筛选(多选)
  69. if len(req.DeliveryEventStatus) > 0 {
  70. db = db.Where(s.EventDao.Columns.DeliveryEventStatus+" in (?)", req.DeliveryEventStatus)
  71. }
  72. // 事件结果筛选(多选)
  73. if len(req.DeliveryEventResult) > 0 {
  74. db = db.Where(s.EventDao.Columns.DeliveryEventResult+" in (?)", req.DeliveryEventResult)
  75. }
  76. // 反馈来源筛选
  77. if req.FeedbackSource != "" {
  78. db = db.Where(s.EventDao.Columns.FeedbackSource, req.FeedbackSource)
  79. }
  80. // 反馈人模糊查询
  81. if req.FeedbackReporter != "" {
  82. db = db.Where(s.EventDao.Columns.FeedbackReporter+" like ?", "%"+req.FeedbackReporter+"%")
  83. }
  84. // 负责人筛选(多选精确匹配)
  85. if len(req.OpsUserName) > 0 {
  86. db = db.Where(s.EventDao.Columns.OpsUserName+" in (?)", req.OpsUserName)
  87. }
  88. // 反馈时间范围
  89. if req.FeedbackDateStart != "" {
  90. db = db.Where(s.EventDao.Columns.FeedbackDate+">= ?", req.FeedbackDateStart)
  91. }
  92. if req.FeedbackDateEnd != "" {
  93. db = db.Where(s.EventDao.Columns.FeedbackDate+"<= ?", req.FeedbackDateEnd)
  94. }
  95. // 处理时间范围
  96. if req.CompleteTimeStart != "" {
  97. db = db.Where(s.EventDao.Columns.CompleteTime+">= ?", req.CompleteTimeStart)
  98. }
  99. if req.CompleteTimeEnd != "" {
  100. db = db.Where(s.EventDao.Columns.CompleteTime+"<= ?", req.CompleteTimeEnd)
  101. }
  102. // 统计总数
  103. total, err = db.Count()
  104. if err != nil {
  105. g.Log().Error(err)
  106. return 0, nil, myerrors.DbError("获取事件总数失败")
  107. }
  108. // 分页查询
  109. pageNum, pageSize := req.GetPage()
  110. var entityList []*eventmodel.OpsDeliveryProjectEvent
  111. // 处理排序
  112. if len(req.SortFields) > 0 {
  113. orderClauses := []string{}
  114. for _, sort := range req.SortFields {
  115. // 将前端字段名转换为数据库列名
  116. colName := s.getSortColumnName(sort.Field)
  117. if colName != "" {
  118. orderClauses = append(orderClauses, colName+" "+strings.ToUpper(sort.Order))
  119. }
  120. }
  121. if len(orderClauses) > 0 {
  122. db = db.Order(strings.Join(orderClauses, ", "))
  123. } else {
  124. db = db.Order(s.EventDao.Columns.CreatedTime + " desc")
  125. }
  126. } else {
  127. db = db.Order(s.EventDao.Columns.CreatedTime + " desc")
  128. }
  129. err = db.Page(pageNum, pageSize).Scan(&entityList)
  130. if err != nil {
  131. g.Log().Error(err)
  132. return 0, nil, myerrors.DbError("查询事件列表失败")
  133. }
  134. // 转换为响应结构体
  135. if err = gconv.Structs(entityList, &list); err != nil {
  136. g.Log().Error(err)
  137. return 0, nil, myerrors.DbError("数据转换失败")
  138. }
  139. if len(list) > 0 {
  140. projectIds := make([]int, 0, len(list))
  141. for _, item := range list {
  142. if item.ProjectId > 0 {
  143. projectIds = append(projectIds, item.ProjectId)
  144. }
  145. }
  146. if len(projectIds) > 0 {
  147. var projects []*eventmodel.OpsDeliveryProject
  148. err := s.ProjectDao.Fields(
  149. s.ProjectDao.Columns.Id,
  150. s.ProjectDao.Columns.ProjectName,
  151. ).WhereIn(s.ProjectDao.Columns.Id, projectIds).Scan(&projects)
  152. if err != nil {
  153. g.Log().Error(err)
  154. } else {
  155. projectMap := make(map[int]string)
  156. for _, p := range projects {
  157. projectMap[p.Id] = p.ProjectName
  158. }
  159. for _, item := range list {
  160. if name, ok := projectMap[item.ProjectId]; ok {
  161. item.ProjectName = name
  162. }
  163. }
  164. }
  165. }
  166. }
  167. return
  168. }
  169. // getSortColumnName 将前端排序字段名转换为数据库列名
  170. func (s *DeliveryProjectEventService) getSortColumnName(field string) string {
  171. // 前端字段名到数据库列名的映射
  172. fieldMap := map[string]string{
  173. "deliveryEventType": s.EventDao.Columns.DeliveryEventType,
  174. "deliveryEventStatus": s.EventDao.Columns.DeliveryEventStatus,
  175. "deliveryEventResult": s.EventDao.Columns.DeliveryEventResult,
  176. "opsUserName": s.EventDao.Columns.OpsUserName,
  177. "feedbackReporter": s.EventDao.Columns.FeedbackReporter,
  178. "feedbackDate": s.EventDao.Columns.FeedbackDate,
  179. "completeTime": s.EventDao.Columns.CompleteTime,
  180. }
  181. if colName, ok := fieldMap[field]; ok {
  182. return colName
  183. }
  184. return ""
  185. }
  186. // Create 新增事件,包含事务控制和过程记录、附件存储
  187. func (s *DeliveryProjectEventService) Create(req *eventmodel.OpsDeliveryProjectEventAddReq) error {
  188. // 生成事件编码
  189. eventNo := s.generateEventNo()
  190. // 硬件发货(40)和硬件安装(41)默认状态为处理中(20)
  191. status := "10" // 待处理
  192. if req.DeliveryEventType == "40" || req.DeliveryEventType == "41" {
  193. status = "20"
  194. }
  195. // 构造数据,负责人默认为当前登录人,允许前端传入
  196. opsUserId := s.GetCxtUserId()
  197. opsUserName := s.GetCxtUserName()
  198. if req.OpsUserId > 0 {
  199. opsUserId = req.OpsUserId
  200. opsUserName = req.OpsUserName
  201. }
  202. data := g.Map{
  203. s.EventDao.Columns.ProjectId: req.ProjectId,
  204. s.EventDao.Columns.DeliveryEventNo: eventNo,
  205. s.EventDao.Columns.DeliveryEventTitle: req.DeliveryEventTitle,
  206. s.EventDao.Columns.DeliveryEventDesc: req.DeliveryEventDesc,
  207. s.EventDao.Columns.DeliveryEventType: req.DeliveryEventType,
  208. s.EventDao.Columns.DeliveryEventStatus: status,
  209. s.EventDao.Columns.FeedbackSource: req.FeedbackSource,
  210. s.EventDao.Columns.FeedbackReporter: req.FeedbackReporter,
  211. s.EventDao.Columns.FeedbackDate: gtime.Now(),
  212. s.EventDao.Columns.OnSite: req.OnSite,
  213. s.EventDao.Columns.OpsUserId: opsUserId,
  214. s.EventDao.Columns.OpsUserName: opsUserName,
  215. }
  216. if req.Attribute1 != "" {
  217. data[s.EventDao.Columns.Attribute1] = req.Attribute1
  218. }
  219. if req.CompleteTime != "" {
  220. data[s.EventDao.Columns.CompleteTime] = req.CompleteTime
  221. }
  222. if req.ActualWorkHour > 0 {
  223. data[s.EventDao.Columns.ActualWorkHour] = req.ActualWorkHour
  224. }
  225. // 补齐审计字段
  226. service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  227. // 使用事务控制
  228. return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  229. // 1. 创建事件记录
  230. result, err := s.EventDao.TX(tx).Data(data).Insert()
  231. if err != nil {
  232. g.Log().Error(err)
  233. return myerrors.DbError("新增事件失败")
  234. }
  235. // 获取新创建的事件ID
  236. eventId, err := result.LastInsertId()
  237. if err != nil {
  238. g.Log().Error(err)
  239. return myerrors.DbError("获取事件ID失败")
  240. }
  241. // 2. 创建事件过程记录
  242. recordData := g.Map{
  243. s.RecordDao.Columns.DeliveryEventId: eventId,
  244. s.RecordDao.Columns.HandleUserId: s.GetCxtUserId(),
  245. s.RecordDao.Columns.HandleUserName: s.GetCxtUserName(),
  246. s.RecordDao.Columns.HandleContent: "创建事件<br/>事件标题: " + req.DeliveryEventTitle,
  247. }
  248. service.SetCreatedInfo(recordData, s.GetCxtUserId(), s.GetCxtUserName())
  249. recordResult, err := s.RecordDao.TX(tx).Data(recordData).Insert()
  250. if err != nil {
  251. g.Log().Error(err)
  252. return myerrors.DbError("新增事件过程记录失败")
  253. }
  254. recordId, err := recordResult.LastInsertId()
  255. if err != nil {
  256. g.Log().Error(err)
  257. return myerrors.DbError("获取过程记录ID失败")
  258. }
  259. // 3. 保存附件信息
  260. if len(req.Attachments) > 0 {
  261. for _, att := range req.Attachments {
  262. attData := g.Map{
  263. s.AttachmentDao.Columns.DeliveryEventId: eventId,
  264. s.AttachmentDao.Columns.EventId: eventId,
  265. s.AttachmentDao.Columns.EventRecordId: recordId,
  266. s.AttachmentDao.Columns.FileName: att.FileName,
  267. s.AttachmentDao.Columns.FileUrl: att.FileUrl,
  268. s.AttachmentDao.Columns.FileType: att.FileType,
  269. }
  270. service.SetCreatedInfo(attData, s.GetCxtUserId(), s.GetCxtUserName())
  271. _, err = s.AttachmentDao.TX(tx).Data(attData).Insert()
  272. if err != nil {
  273. g.Log().Error(err)
  274. return myerrors.DbError("保存附件信息失败")
  275. }
  276. }
  277. }
  278. // 4. 特定事件类型自动创建研发任务
  279. if taskType, ok := eventTypeToAutoTaskType[req.DeliveryEventType]; ok {
  280. taskNo := s.generateTaskNo()
  281. taskData := g.Map{
  282. s.TaskDao.Columns.TaskNo: taskNo,
  283. s.TaskDao.Columns.ProjectId: req.ProjectId,
  284. s.TaskDao.Columns.ProjectName: s.getProjectName(req.ProjectId),
  285. s.TaskDao.Columns.TaskTitle: req.DeliveryEventTitle,
  286. s.TaskDao.Columns.TaskDesc: req.DeliveryEventDesc,
  287. s.TaskDao.Columns.FunctionName: req.DeliveryEventTitle,
  288. s.TaskDao.Columns.TaskType: taskType,
  289. s.TaskDao.Columns.TaskStatus: eventmodel.TaskStatusTodo,
  290. s.TaskDao.Columns.Priority: "20",
  291. s.TaskDao.Columns.EventId: int(eventId),
  292. s.TaskDao.Columns.EventType: eventmodel.EventTypeDelivery,
  293. }
  294. service.SetCreatedInfo(taskData, s.GetCxtUserId(), s.GetCxtUserName())
  295. taskResult, err := s.TaskDao.TX(tx).Data(taskData).Insert()
  296. if err != nil {
  297. g.Log().Error(err)
  298. return myerrors.DbError("自动创建研发任务失败")
  299. }
  300. taskId, _ := taskResult.LastInsertId()
  301. taskRecordData := g.Map{
  302. s.TaskRecordDao.Columns.TaskId: taskId,
  303. s.TaskRecordDao.Columns.HandleUserId: s.GetCxtUserId(),
  304. s.TaskRecordDao.Columns.HandleUserName: s.GetCxtUserName(),
  305. s.TaskRecordDao.Columns.HandleContent: "自动创建任务<br/>说明: 由交付事件自动创建 (" + req.DeliveryEventTitle + ")",
  306. }
  307. service.SetCreatedInfo(taskRecordData, s.GetCxtUserId(), s.GetCxtUserName())
  308. _, err = s.TaskRecordDao.TX(tx).Data(taskRecordData).Insert()
  309. if err != nil {
  310. g.Log().Error(err)
  311. return myerrors.DbError("新增任务过程记录失败")
  312. }
  313. }
  314. return nil
  315. })
  316. }
  317. // UpdateById 根据ID更新事件
  318. func (s *DeliveryProjectEventService) UpdateById(req *eventmodel.OpsDeliveryProjectEventUpdateReq) error {
  319. // 校验数据是否存在
  320. var entity eventmodel.OpsDeliveryProjectEvent
  321. err := s.EventDao.FieldsEx(s.EventDao.Columns.DeletedTime).WherePri(s.EventDao.Columns.Id, req.Id).Scan(&entity)
  322. if err != nil {
  323. g.Log().Error(err)
  324. return myerrors.DbError("查询事件数据失败")
  325. }
  326. if entity.Id <= 0 {
  327. return myerrors.TipsError("事件数据不存在")
  328. }
  329. // 构造更新数据 - 从请求中动态构建,支持前端传来的任何有效字段
  330. data := g.Map{}
  331. // 基础字段(编辑时可能更新,但关闭/处理事件时不传这些字段)
  332. if req.DeliveryEventTitle != "" {
  333. data[s.EventDao.Columns.DeliveryEventTitle] = req.DeliveryEventTitle
  334. }
  335. if req.DeliveryEventDesc != "" {
  336. data[s.EventDao.Columns.DeliveryEventDesc] = req.DeliveryEventDesc
  337. }
  338. if req.DeliveryEventType != "" {
  339. data[s.EventDao.Columns.DeliveryEventType] = req.DeliveryEventType
  340. }
  341. if req.FeedbackSource != "" {
  342. data[s.EventDao.Columns.FeedbackSource] = req.FeedbackSource
  343. }
  344. if req.FeedbackReporter != "" {
  345. data[s.EventDao.Columns.FeedbackReporter] = req.FeedbackReporter
  346. }
  347. if req.OnSite != "" {
  348. data[s.EventDao.Columns.OnSite] = req.OnSite
  349. }
  350. // 处理状态和结果字段(关闭事件时传入)
  351. if req.DeliveryEventStatus != "" {
  352. data[s.EventDao.Columns.DeliveryEventStatus] = req.DeliveryEventStatus
  353. }
  354. if req.DeliveryEventResult != "" {
  355. data[s.EventDao.Columns.DeliveryEventResult] = req.DeliveryEventResult
  356. }
  357. // 处理完成相关字段(处理/关闭事件时传入)
  358. if req.CompleteDesc != "" {
  359. data[s.EventDao.Columns.CompleteDesc] = req.CompleteDesc
  360. }
  361. if req.CompleteTime != "" {
  362. data[s.EventDao.Columns.CompleteTime] = req.CompleteTime
  363. }
  364. if req.ActualWorkHour > 0 {
  365. data[s.EventDao.Columns.ActualWorkHour] = req.ActualWorkHour
  366. }
  367. // 处理负责人相关字段(分配时传入)
  368. if req.OpsUserId > 0 {
  369. data[s.EventDao.Columns.OpsUserId] = req.OpsUserId
  370. data[s.EventDao.Columns.OpsUserName] = req.OpsUserName
  371. }
  372. // 物流单号
  373. if req.Attribute1 != "" {
  374. data[s.EventDao.Columns.Attribute1] = req.Attribute1
  375. }
  376. // 补齐审计字段
  377. service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  378. // 更新操作,排除不可改字段
  379. _, err = s.EventDao.FieldsEx(service.UpdateFieldEx...).Data(data).WherePri(s.EventDao.Columns.Id, req.Id).Update()
  380. if err != nil {
  381. g.Log().Error(err)
  382. return myerrors.DbError("更新事件失败")
  383. }
  384. return nil
  385. }
  386. // DeleteByIds 根据ID批量删除
  387. func (s *DeliveryProjectEventService) DeleteByIds(ids []int64) error {
  388. if len(ids) == 0 {
  389. return myerrors.TipsError("请选择需要删除的事件")
  390. }
  391. return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  392. // 1. 删除事件
  393. _, err := s.EventDao.TX(tx).WhereIn(s.EventDao.Columns.Id, ids).Delete()
  394. if err != nil {
  395. g.Log().Error(err)
  396. return myerrors.DbError("删除事件失败")
  397. }
  398. // 2. 删除关联的过程记录
  399. _, err = s.RecordDao.TX(tx).WhereIn(s.RecordDao.Columns.DeliveryEventId, ids).Delete()
  400. if err != nil {
  401. g.Log().Error(err)
  402. return myerrors.DbError("删除事件过程记录失败")
  403. }
  404. // 3. 删除关联的附件
  405. _, err = s.AttachmentDao.TX(tx).WhereIn(s.AttachmentDao.Columns.DeliveryEventId, ids).Delete()
  406. if err != nil {
  407. g.Log().Error(err)
  408. return myerrors.DbError("删除事件附件失败")
  409. }
  410. return nil
  411. })
  412. }
  413. // Cancel 作废事件
  414. func (s *DeliveryProjectEventService) Cancel(req *eventmodel.OpsDeliveryProjectEventCancelReq) error {
  415. // 校验数据是否存在
  416. var entity eventmodel.OpsDeliveryProjectEvent
  417. err := s.EventDao.FieldsEx(s.EventDao.Columns.DeletedTime).WherePri(s.EventDao.Columns.Id, req.Id).Scan(&entity)
  418. if err != nil {
  419. g.Log().Error(err)
  420. return myerrors.DbError("查询事件数据失败")
  421. }
  422. if entity.Id <= 0 {
  423. return myerrors.TipsError("事件数据不存在")
  424. }
  425. // 已关闭的事件不能作废
  426. if entity.DeliveryEventStatus == eventmodel.DeliveryEventStatusClosed {
  427. return myerrors.TipsError("已关闭的事件不能作废")
  428. }
  429. // 已作废的事件不能再次作废
  430. if entity.DeliveryEventStatus == eventmodel.DeliveryEventStatusCancelled {
  431. return myerrors.TipsError("该事件已作废")
  432. }
  433. return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  434. // 1. 更新事件状态为已作废
  435. data := g.Map{
  436. s.EventDao.Columns.DeliveryEventStatus: eventmodel.DeliveryEventStatusCancelled,
  437. s.EventDao.Columns.Remark: req.CancelReason,
  438. }
  439. service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  440. _, err := s.EventDao.TX(tx).FieldsEx(service.UpdateFieldEx...).Data(data).WherePri(s.EventDao.Columns.Id, req.Id).Update()
  441. if err != nil {
  442. g.Log().Error(err)
  443. return myerrors.DbError("作废事件失败")
  444. }
  445. // 2. 添加作废过程记录
  446. recordContent := "作废事件<br/>作废原因: " + req.CancelReason
  447. recordData := g.Map{
  448. s.RecordDao.Columns.DeliveryEventId: req.Id,
  449. s.RecordDao.Columns.HandleUserId: s.GetCxtUserId(),
  450. s.RecordDao.Columns.HandleUserName: s.GetCxtUserName(),
  451. s.RecordDao.Columns.HandleContent: recordContent,
  452. }
  453. service.SetCreatedInfo(recordData, s.GetCxtUserId(), s.GetCxtUserName())
  454. recordResult, err := s.RecordDao.TX(tx).Data(recordData).Insert()
  455. if err != nil {
  456. g.Log().Error(err)
  457. return myerrors.DbError("新增过程记录失败")
  458. }
  459. recordId, _ := recordResult.LastInsertId()
  460. // 3. 保存附件
  461. if len(req.Attachments) > 0 {
  462. for _, att := range req.Attachments {
  463. attData := g.Map{
  464. s.AttachmentDao.Columns.DeliveryEventId: req.Id,
  465. s.AttachmentDao.Columns.EventId: req.Id,
  466. s.AttachmentDao.Columns.EventRecordId: recordId,
  467. s.AttachmentDao.Columns.FileName: att.FileName,
  468. s.AttachmentDao.Columns.FileUrl: att.FileUrl,
  469. s.AttachmentDao.Columns.FileType: att.FileType,
  470. }
  471. service.SetCreatedInfo(attData, s.GetCxtUserId(), s.GetCxtUserName())
  472. _, err = s.AttachmentDao.TX(tx).Data(attData).Insert()
  473. if err != nil {
  474. g.Log().Error(err)
  475. return myerrors.DbError("保存附件信息失败")
  476. }
  477. }
  478. }
  479. _ = recordId // 避免未使用变量警告
  480. // 4. 查询关联的任务并作废
  481. var tasks []*eventmodel.OpsEventTask
  482. err = s.TaskDao.TX(tx).Where(s.TaskDao.Columns.EventId, req.Id).
  483. Where(s.TaskDao.Columns.EventType, eventmodel.EventTypeDelivery).
  484. Scan(&tasks)
  485. if err != nil {
  486. g.Log().Error(err)
  487. return myerrors.DbError("查询关联任务失败")
  488. }
  489. // 5. 作废关联的任务
  490. for _, task := range tasks {
  491. // 已完成的任务不能作废
  492. if task.TaskStatus == "30" {
  493. continue
  494. }
  495. // 已作废的任务不需要再次作废
  496. if task.TaskStatus == "90" {
  497. continue
  498. }
  499. // 更新任务状态为作废
  500. taskData := g.Map{
  501. s.TaskDao.Columns.TaskStatus: "90",
  502. }
  503. service.SetUpdatedInfo(taskData, s.GetCxtUserId(), s.GetCxtUserName())
  504. _, err := s.TaskDao.TX(tx).FieldsEx(service.UpdateFieldEx...).Data(taskData).
  505. WherePri(s.TaskDao.Columns.Id, task.Id).Update()
  506. if err != nil {
  507. g.Log().Error(err)
  508. return myerrors.DbError("作废关联任务失败")
  509. }
  510. // 添加任务作废过程记录
  511. taskRecordData := g.Map{
  512. s.TaskRecordDao.Columns.TaskId: task.Id,
  513. s.TaskRecordDao.Columns.HandleUserId: s.GetCxtUserId(),
  514. s.TaskRecordDao.Columns.HandleUserName: s.GetCxtUserName(),
  515. s.TaskRecordDao.Columns.HandleContent: "作废任务<br/>作废原因: 关联事件被作废 - " + req.CancelReason,
  516. }
  517. service.SetCreatedInfo(taskRecordData, s.GetCxtUserId(), s.GetCxtUserName())
  518. _, err = s.TaskRecordDao.TX(tx).Data(taskRecordData).Insert()
  519. if err != nil {
  520. g.Log().Error(err)
  521. return myerrors.DbError("新增任务过程记录失败")
  522. }
  523. }
  524. return nil
  525. })
  526. }
  527. // GetById 根据ID查询单条(关联项目信息)
  528. func (s *DeliveryProjectEventService) GetById(id int) (*eventmodel.OpsDeliveryProjectEventRsp, error) {
  529. var entity eventmodel.OpsDeliveryProjectEvent
  530. err := s.EventDao.FieldsEx(s.EventDao.Columns.DeletedTime).WherePri(s.EventDao.Columns.Id, id).Scan(&entity)
  531. if err != nil {
  532. g.Log().Error(err)
  533. return nil, myerrors.DbError("查询事件数据失败")
  534. }
  535. if entity.Id <= 0 {
  536. return nil, myerrors.TipsError("事件数据不存在")
  537. }
  538. var rsp eventmodel.OpsDeliveryProjectEventRsp
  539. if err := gconv.Struct(entity, &rsp); err != nil {
  540. g.Log().Error(err)
  541. return nil, myerrors.DbError("数据转换失败")
  542. }
  543. // 查询关联的项目信息
  544. if entity.ProjectId > 0 {
  545. var project eventmodel.OpsDeliveryProject
  546. err := s.ProjectDao.FieldsEx(s.ProjectDao.Columns.DeletedTime).
  547. WherePri(s.ProjectDao.Columns.Id, entity.ProjectId).
  548. Scan(&project)
  549. if err != nil {
  550. g.Log().Error(err)
  551. } else if project.Id > 0 {
  552. // 填充项目信息
  553. rsp.ProjectName = project.ProjectName
  554. rsp.ContractNo = project.ContractNo
  555. rsp.CustName = project.CustName
  556. rsp.ProductLine = project.ProductLine
  557. rsp.SalesUserName = project.SalesUserName
  558. // 填充关键节点时间
  559. if project.InternalKickoffTime != nil {
  560. rsp.InternalKickoffTime = project.InternalKickoffTime.Format("Y-m-d H:i:s")
  561. }
  562. if project.ExternalKickoffTime != nil {
  563. rsp.ExternalKickoffTime = project.ExternalKickoffTime.Format("Y-m-d H:i:s")
  564. }
  565. if project.DeliveryPlanSubmitTime != nil {
  566. rsp.DeliveryPlanSubmitTime = project.DeliveryPlanSubmitTime.Format("Y-m-d H:i:s")
  567. }
  568. if project.DeploymentTime != nil {
  569. rsp.DeploymentTime = project.DeploymentTime.Format("Y-m-d H:i:s")
  570. }
  571. if project.TrialRunTime != nil {
  572. rsp.TrialRunTime = project.TrialRunTime.Format("Y-m-d H:i:s")
  573. }
  574. if project.GoLiveTime != nil {
  575. rsp.GoLiveTime = project.GoLiveTime.Format("Y-m-d H:i:s")
  576. }
  577. }
  578. }
  579. return &rsp, nil
  580. }
  581. // GetAttachments 获取事件附件列表
  582. func (s *DeliveryProjectEventService) GetAttachments(eventId int) ([]*eventmodel.OpsDeliveryProjectEventAttachment, error) {
  583. var list []*eventmodel.OpsDeliveryProjectEventAttachment
  584. err := s.AttachmentDao.Where(s.AttachmentDao.Columns.DeliveryEventId, eventId).Scan(&list)
  585. if err != nil {
  586. g.Log().Error(err)
  587. return nil, myerrors.DbError("查询附件列表失败")
  588. }
  589. return list, nil
  590. }
  591. // GetRecords 获取事件过程记录列表(包含附件)
  592. func (s *DeliveryProjectEventService) GetRecords(req *eventmodel.OpsDeliveryProjectEventRecordSearchReq) ([]*eventmodel.OpsDeliveryProjectEventRecordWithAttachments, error) {
  593. var records []*eventmodel.OpsDeliveryProjectEventRecord
  594. err := s.RecordDao.Where(s.RecordDao.Columns.DeliveryEventId, req.DeliveryEventId).
  595. Order(s.RecordDao.Columns.CreatedTime + " desc").
  596. Scan(&records)
  597. if err != nil {
  598. g.Log().Error(err)
  599. return nil, myerrors.DbError("查询过程记录失败")
  600. }
  601. result := make([]*eventmodel.OpsDeliveryProjectEventRecordWithAttachments, 0, len(records))
  602. for _, record := range records {
  603. recordRsp := &eventmodel.OpsDeliveryProjectEventRecordWithAttachments{
  604. OpsDeliveryProjectEventRecord: *record,
  605. Attachments: []*eventmodel.OpsDeliveryProjectEventAttachment{},
  606. }
  607. // 查询该记录关联的附件
  608. if record.Id > 0 {
  609. var attachments []*eventmodel.OpsDeliveryProjectEventAttachment
  610. err := s.AttachmentDao.Where(s.AttachmentDao.Columns.EventRecordId, record.Id).
  611. Scan(&attachments)
  612. if err != nil {
  613. g.Log().Error(err)
  614. } else {
  615. recordRsp.Attachments = attachments
  616. }
  617. }
  618. result = append(result, recordRsp)
  619. }
  620. return result, nil
  621. }
  622. // AddRecord 添加事件过程记录(带附件)
  623. func (s *DeliveryProjectEventService) AddRecord(req *eventmodel.OpsDeliveryProjectEventRecordAddReqWithAttachments) error {
  624. return s.EventDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  625. // 1. 创建过程记录
  626. recordData := g.Map{
  627. s.RecordDao.Columns.DeliveryEventId: req.DeliveryEventId,
  628. s.RecordDao.Columns.HandleUserId: s.GetCxtUserId(),
  629. s.RecordDao.Columns.HandleUserName: s.GetCxtUserName(),
  630. s.RecordDao.Columns.HandleContent: req.HandleContent,
  631. }
  632. service.SetCreatedInfo(recordData, s.GetCxtUserId(), s.GetCxtUserName())
  633. result, err := s.RecordDao.TX(tx).Data(recordData).Insert()
  634. if err != nil {
  635. g.Log().Error(err)
  636. return myerrors.DbError("新增过程记录失败")
  637. }
  638. // 获取记录ID
  639. recordId, err := result.LastInsertId()
  640. if err != nil {
  641. g.Log().Error(err)
  642. return myerrors.DbError("获取记录ID失败")
  643. }
  644. // 2. 保存附件
  645. if len(req.Attachments) > 0 {
  646. for _, att := range req.Attachments {
  647. attData := g.Map{
  648. s.AttachmentDao.Columns.DeliveryEventId: req.DeliveryEventId,
  649. s.AttachmentDao.Columns.EventId: req.DeliveryEventId,
  650. s.AttachmentDao.Columns.EventRecordId: recordId,
  651. s.AttachmentDao.Columns.FileName: att.FileName,
  652. s.AttachmentDao.Columns.FileUrl: att.FileUrl,
  653. s.AttachmentDao.Columns.FileType: att.FileType,
  654. }
  655. service.SetCreatedInfo(attData, s.GetCxtUserId(), s.GetCxtUserName())
  656. _, err = s.AttachmentDao.TX(tx).Data(attData).Insert()
  657. if err != nil {
  658. g.Log().Error(err)
  659. return myerrors.DbError("保存附件信息失败")
  660. }
  661. }
  662. }
  663. return nil
  664. })
  665. }
  666. // generateTaskNo 生成任务编号(与OpsEventTaskService保持统一格式)
  667. func (s *DeliveryProjectEventService) generateTaskNo() string {
  668. now := gtime.Now()
  669. prefix := "TSK" + now.Format("Ymd")
  670. var maxNoResult struct {
  671. TaskNo string
  672. }
  673. err := s.TaskDao.Where(s.TaskDao.Columns.TaskNo+" like ?", prefix+"%").
  674. Order(s.TaskDao.Columns.TaskNo + " desc").
  675. Fields(s.TaskDao.Columns.TaskNo).
  676. Scan(&maxNoResult)
  677. if err != nil || maxNoResult.TaskNo == "" {
  678. return prefix + "0001"
  679. }
  680. maxNoStr := maxNoResult.TaskNo
  681. if len(maxNoStr) >= len(prefix)+4 {
  682. seq := maxNoStr[len(prefix):]
  683. seqNum := gconv.Int(seq)
  684. seqNum++
  685. return prefix + fmt.Sprintf("%04d", seqNum)
  686. }
  687. return prefix + "0001"
  688. }
  689. // getProjectName 根据项目ID获取项目名称
  690. func (s *DeliveryProjectEventService) getProjectName(projectId int) string {
  691. if projectId <= 0 {
  692. return ""
  693. }
  694. var project eventmodel.OpsDeliveryProject
  695. err := s.ProjectDao.FieldsEx(s.ProjectDao.Columns.DeletedTime).
  696. WherePri(s.ProjectDao.Columns.Id, projectId).
  697. Scan(&project)
  698. if err != nil {
  699. g.Log().Error(err)
  700. return ""
  701. }
  702. return project.ProjectName
  703. }
  704. // generateEventNo 生成事件编码
  705. func (s *DeliveryProjectEventService) generateEventNo() string {
  706. // 格式: EVT + 年月日 + 4位序号
  707. now := gtime.Now()
  708. prefix := "EVT" + now.Format("Ymd")
  709. // 查询当天最大序号
  710. var maxNoResult struct {
  711. DeliveryEventNo string
  712. }
  713. err := s.EventDao.Where(s.EventDao.Columns.DeliveryEventNo+" like ?", prefix+"%").
  714. Order(s.EventDao.Columns.DeliveryEventNo + " desc").
  715. Fields(s.EventDao.Columns.DeliveryEventNo).
  716. Scan(&maxNoResult)
  717. if err != nil || maxNoResult.DeliveryEventNo == "" {
  718. return prefix + "0001"
  719. }
  720. // 提取序号并+1
  721. maxNoStr := maxNoResult.DeliveryEventNo
  722. if len(maxNoStr) >= len(prefix)+4 {
  723. seq := maxNoStr[len(prefix):]
  724. seqNum := gconv.Int(seq)
  725. seqNum++
  726. return prefix + fmt.Sprintf("%04d", seqNum)
  727. }
  728. return prefix + "0001"
  729. }