plat_meeting.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. package opsdev
  2. import (
  3. "context"
  4. "dashoo.cn/opms_libary/myerrors"
  5. opsdevdao "dashoo.cn/opms_parent/app/dao/opsdev"
  6. opsdevmodel "dashoo.cn/opms_parent/app/model/opsdev"
  7. "dashoo.cn/opms_parent/app/service"
  8. "github.com/gogf/gf/database/gdb"
  9. "github.com/gogf/gf/frame/g"
  10. "github.com/gogf/gf/os/gtime"
  11. "github.com/gogf/gf/util/gconv"
  12. )
  13. // PlatMeetingService 会议业务逻辑实现类
  14. type PlatMeetingService struct {
  15. *service.ContextService
  16. MeetingDao *opsdevdao.PlatMeetingDao
  17. AttendeeDao *opsdevdao.PlatMeetingAttendeeDao
  18. WorkHourDao *opsdevdao.PlatMeetingWorkHourDao
  19. }
  20. // NewPlatMeetingService 初始化service
  21. func NewPlatMeetingService(ctx context.Context) (svc *PlatMeetingService, err error) {
  22. svc = new(PlatMeetingService)
  23. if svc.ContextService, err = svc.Init(ctx); err != nil {
  24. return nil, err
  25. }
  26. svc.MeetingDao = opsdevdao.NewPlatMeetingDao(svc.Tenant)
  27. svc.AttendeeDao = opsdevdao.NewPlatMeetingAttendeeDao(svc.Tenant)
  28. svc.WorkHourDao = opsdevdao.NewPlatMeetingWorkHourDao(svc.Tenant)
  29. return svc, nil
  30. }
  31. // GetList 分页查询会议列表
  32. func (s *PlatMeetingService) GetList(req *opsdevmodel.PlatMeetingSearchReq) (total int, list []*opsdevmodel.PlatMeetingRsp, err error) {
  33. db := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime)
  34. if req.MeetingTitle != "" {
  35. db = db.Where(s.MeetingDao.Columns.MeetingTitle+" like ?", "%"+req.MeetingTitle+"%")
  36. }
  37. if req.OrganizerName != "" {
  38. db = db.Where(s.MeetingDao.Columns.OrganizerName+" like ?", "%"+req.OrganizerName+"%")
  39. }
  40. if req.DeptId > 0 {
  41. db = db.Where(s.MeetingDao.Columns.DeptId, req.DeptId)
  42. }
  43. if req.MeetingDateStart != "" {
  44. db = db.Where(s.MeetingDao.Columns.MeetingDate+" >= ?", req.MeetingDateStart)
  45. }
  46. if req.MeetingDateEnd != "" {
  47. db = db.Where(s.MeetingDao.Columns.MeetingDate+" <= ?", req.MeetingDateEnd)
  48. }
  49. total, err = db.Count()
  50. if err != nil {
  51. return 0, nil, myerrors.DbError("获取会议总数失败")
  52. }
  53. pageNum, pageSize := req.GetPage()
  54. db = db.Order(s.MeetingDao.Columns.CreatedTime + " desc")
  55. var entities []*opsdevmodel.PlatMeeting
  56. err = db.Page(pageNum, pageSize).Scan(&entities)
  57. if err != nil {
  58. return 0, nil, myerrors.DbError("查询会议列表失败")
  59. }
  60. if err = gconv.Structs(entities, &list); err != nil {
  61. return 0, nil, myerrors.DbError("数据转换失败")
  62. }
  63. // 查询每个会议的参会人员
  64. for _, item := range list {
  65. attendees, _ := s.getAttendeesByMeetingId(int(item.Id))
  66. item.Attendees = attendees
  67. }
  68. return
  69. }
  70. // GetById 根据ID获取会议详情
  71. func (s *PlatMeetingService) GetById(id int) (*opsdevmodel.PlatMeetingRsp, error) {
  72. var entity opsdevmodel.PlatMeeting
  73. err := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime).
  74. WherePri(s.MeetingDao.Columns.Id, id).Scan(&entity)
  75. if err != nil {
  76. return nil, myerrors.DbError("查询会议失败")
  77. }
  78. if entity.Id <= 0 {
  79. return nil, myerrors.TipsError("会议不存在")
  80. }
  81. var rsp opsdevmodel.PlatMeetingRsp
  82. if err := gconv.Struct(entity, &rsp); err != nil {
  83. return nil, myerrors.DbError("数据转换失败")
  84. }
  85. attendees, err := s.getAttendeesByMeetingId(id)
  86. if err != nil {
  87. return nil, err
  88. }
  89. rsp.Attendees = attendees
  90. return &rsp, nil
  91. }
  92. // Create 新增会议(含参会人员 + 工时生成)
  93. func (s *PlatMeetingService) Create(req *opsdevmodel.PlatMeetingAddReq) error {
  94. data := g.Map{
  95. s.MeetingDao.Columns.MeetingTitle: req.MeetingTitle,
  96. s.MeetingDao.Columns.MeetingContent: req.MeetingContent,
  97. s.MeetingDao.Columns.OrganizerId: req.OrganizerId,
  98. s.MeetingDao.Columns.OrganizerName: req.OrganizerName,
  99. s.MeetingDao.Columns.DeptId: req.DeptId,
  100. s.MeetingDao.Columns.Duration: req.Duration,
  101. s.MeetingDao.Columns.Remark: req.Remark,
  102. }
  103. if req.MeetingDate != "" {
  104. data[s.MeetingDao.Columns.MeetingDate] = req.MeetingDate
  105. }
  106. service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  107. return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  108. result, err := s.MeetingDao.TX(tx).Data(data).Insert()
  109. if err != nil {
  110. return myerrors.DbError("新增会议失败")
  111. }
  112. meetingId, err := result.LastInsertId()
  113. if err != nil {
  114. return myerrors.DbError("获取会议ID失败")
  115. }
  116. if err := s.batchInsertAttendees(tx, int(meetingId), req.UserIds, req.UserNames); err != nil {
  117. return err
  118. }
  119. // 生成发起人工时
  120. workDate := gtime.Now()
  121. if req.MeetingDate != "" {
  122. workDate = gtime.NewFromStr(req.MeetingDate)
  123. }
  124. orgWhData := g.Map{
  125. s.WorkHourDao.Columns.MeetingId: int(meetingId),
  126. s.WorkHourDao.Columns.AttendeeId: 0,
  127. s.WorkHourDao.Columns.UserId: req.OrganizerId,
  128. s.WorkHourDao.Columns.UserName: req.OrganizerName,
  129. s.WorkHourDao.Columns.WorkDate: workDate,
  130. s.WorkHourDao.Columns.WorkHour: req.Duration,
  131. s.WorkHourDao.Columns.Remark: req.MeetingTitle,
  132. }
  133. service.SetCreatedInfo(orgWhData, s.GetCxtUserId(), s.GetCxtUserName())
  134. if _, err := s.WorkHourDao.TX(tx).Data(orgWhData).Insert(); err != nil {
  135. return myerrors.DbError("生成发起人工时失败")
  136. }
  137. // 生成参会人员工时
  138. attendees, err := s.AttendeeDao.TX(tx).FieldsEx(s.AttendeeDao.Columns.DeletedTime).
  139. Where(s.AttendeeDao.Columns.MeetingId, int(meetingId)).All()
  140. if err != nil {
  141. return myerrors.DbError("查询参会人员失败")
  142. }
  143. for _, row := range attendees {
  144. whData := g.Map{
  145. s.WorkHourDao.Columns.MeetingId: int(meetingId),
  146. s.WorkHourDao.Columns.AttendeeId: row.Id,
  147. s.WorkHourDao.Columns.UserId: row.UserId,
  148. s.WorkHourDao.Columns.UserName: row.UserName,
  149. s.WorkHourDao.Columns.WorkDate: workDate,
  150. s.WorkHourDao.Columns.WorkHour: req.Duration,
  151. s.WorkHourDao.Columns.Remark: req.MeetingTitle,
  152. }
  153. service.SetCreatedInfo(whData, s.GetCxtUserId(), s.GetCxtUserName())
  154. if _, err := s.WorkHourDao.TX(tx).Data(whData).Insert(); err != nil {
  155. return myerrors.DbError("生成参会人工时失败")
  156. }
  157. }
  158. // 标记参会人工时已生成
  159. if _, err := s.AttendeeDao.TX(tx).
  160. Data(g.Map{s.AttendeeDao.Columns.WorkHourGenerated: 1}).
  161. Where(s.AttendeeDao.Columns.MeetingId, int(meetingId)).Update(); err != nil {
  162. return myerrors.DbError("更新参会人标记失败")
  163. }
  164. return nil
  165. })
  166. }
  167. // UpdateById 更新会议
  168. func (s *PlatMeetingService) UpdateById(req *opsdevmodel.PlatMeetingUpdateReq) error {
  169. var entity opsdevmodel.PlatMeeting
  170. err := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime).
  171. WherePri(s.MeetingDao.Columns.Id, req.Id).Scan(&entity)
  172. if err != nil {
  173. return myerrors.DbError("查询会议失败")
  174. }
  175. if entity.Id <= 0 {
  176. return myerrors.TipsError("会议不存在")
  177. }
  178. data := g.Map{}
  179. if req.MeetingTitle != "" {
  180. data[s.MeetingDao.Columns.MeetingTitle] = req.MeetingTitle
  181. }
  182. if req.MeetingContent != "" {
  183. data[s.MeetingDao.Columns.MeetingContent] = req.MeetingContent
  184. }
  185. if req.MeetingDate != "" {
  186. data[s.MeetingDao.Columns.MeetingDate] = req.MeetingDate
  187. }
  188. if req.Duration > 0 {
  189. data[s.MeetingDao.Columns.Duration] = req.Duration
  190. }
  191. if req.OrganizerId > 0 {
  192. data[s.MeetingDao.Columns.OrganizerId] = req.OrganizerId
  193. }
  194. if req.OrganizerName != "" {
  195. data[s.MeetingDao.Columns.OrganizerName] = req.OrganizerName
  196. }
  197. if req.DeptId > 0 {
  198. data[s.MeetingDao.Columns.DeptId] = req.DeptId
  199. }
  200. if req.Remark != "" {
  201. data[s.MeetingDao.Columns.Remark] = req.Remark
  202. }
  203. if len(data) == 0 && len(req.UserIds) == 0 {
  204. return nil
  205. }
  206. service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  207. return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  208. if len(data) > 0 {
  209. _, err = s.MeetingDao.TX(tx).FieldsEx(service.UpdateFieldEx...).
  210. Data(data).WherePri(s.MeetingDao.Columns.Id, req.Id).Update()
  211. if err != nil {
  212. return myerrors.DbError("更新会议失败")
  213. }
  214. }
  215. // 全量替换参会人员
  216. if len(req.UserIds) > 0 {
  217. if _, err := s.AttendeeDao.TX(tx).
  218. Where(s.AttendeeDao.Columns.MeetingId, req.Id).Delete(); err != nil {
  219. return myerrors.DbError("清除旧参会人员失败")
  220. }
  221. if err := s.batchInsertAttendees(tx, req.Id, req.UserIds, req.UserNames); err != nil {
  222. return err
  223. }
  224. }
  225. return nil
  226. })
  227. }
  228. // DeleteByIds 删除会议(软删除会议及参会人员)
  229. func (s *PlatMeetingService) DeleteByIds(ids []int64) error {
  230. if len(ids) == 0 {
  231. return myerrors.TipsError("请选择需要删除的记录")
  232. }
  233. return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  234. // 软删除参会人员
  235. for _, id := range ids {
  236. if _, err := s.AttendeeDao.TX(tx).
  237. Where(s.AttendeeDao.Columns.MeetingId, id).Delete(); err != nil {
  238. return myerrors.DbError("删除参会人员失败")
  239. }
  240. }
  241. // 软删除会议
  242. if _, err := s.MeetingDao.TX(tx).WhereIn(s.MeetingDao.Columns.Id, ids).Delete(); err != nil {
  243. return myerrors.DbError("删除会议失败")
  244. }
  245. return nil
  246. })
  247. }
  248. // AddAttendees 追加参会人员
  249. func (s *PlatMeetingService) AddAttendees(req *opsdevmodel.PlatMeetingAddAttendeeReq) error {
  250. // 校验会议存在
  251. var entity opsdevmodel.PlatMeeting
  252. err := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime).
  253. WherePri(s.MeetingDao.Columns.Id, req.MeetingId).Scan(&entity)
  254. if err != nil {
  255. return myerrors.DbError("查询会议失败")
  256. }
  257. if entity.Id <= 0 {
  258. return myerrors.TipsError("会议不存在")
  259. }
  260. return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  261. // 查询已存在参会人,避免重复插入
  262. existing, err := s.AttendeeDao.TX(tx).
  263. Where(s.AttendeeDao.Columns.MeetingId, req.MeetingId).
  264. WhereIn(s.AttendeeDao.Columns.UserId, req.UserIds).
  265. Fields(s.AttendeeDao.Columns.UserId).All()
  266. if err != nil {
  267. return myerrors.DbError("查询已有参会人员失败")
  268. }
  269. existingIds := make(map[int]bool)
  270. for _, row := range existing {
  271. existingIds[row["user_id"].Int()] = true
  272. }
  273. for i, userId := range req.UserIds {
  274. if existingIds[userId] {
  275. continue
  276. }
  277. userName := ""
  278. if i < len(req.UserNames) {
  279. userName = req.UserNames[i]
  280. }
  281. attendeeData := g.Map{
  282. s.AttendeeDao.Columns.MeetingId: req.MeetingId,
  283. s.AttendeeDao.Columns.UserId: userId,
  284. s.AttendeeDao.Columns.UserName: userName,
  285. }
  286. service.SetCreatedInfo(attendeeData, s.GetCxtUserId(), s.GetCxtUserName())
  287. if _, err := s.AttendeeDao.TX(tx).Data(attendeeData).Insert(); err != nil {
  288. return myerrors.DbError("新增参会人员失败")
  289. }
  290. }
  291. return nil
  292. })
  293. }
  294. // Complete 结束会议——为发起人和参会人员补生成工时
  295. func (s *PlatMeetingService) Complete(req *opsdevmodel.PlatMeetingCompleteReq) error {
  296. var entity opsdevmodel.PlatMeeting
  297. err := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime).
  298. WherePri(s.MeetingDao.Columns.Id, req.Id).Scan(&entity)
  299. if err != nil {
  300. return myerrors.DbError("查询会议失败")
  301. }
  302. if entity.Id <= 0 {
  303. return myerrors.TipsError("会议不存在")
  304. }
  305. if entity.Duration <= 0 {
  306. return myerrors.TipsError("会议时长未设置,无法生成工时")
  307. }
  308. return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
  309. workDate := gtime.Now()
  310. if entity.MeetingDate != nil {
  311. workDate = entity.MeetingDate
  312. }
  313. // 检查发起人工时是否已存在
  314. orgCount, err := s.WorkHourDao.TX(tx).FieldsEx(s.WorkHourDao.Columns.DeletedTime).
  315. Where(s.WorkHourDao.Columns.MeetingId, req.Id).
  316. Where(s.WorkHourDao.Columns.UserId, entity.OrganizerId).
  317. Where(s.WorkHourDao.Columns.AttendeeId, 0).Count()
  318. if err != nil {
  319. return myerrors.DbError("查询发起人工时失败")
  320. }
  321. if orgCount == 0 {
  322. orgWhData := g.Map{
  323. s.WorkHourDao.Columns.MeetingId: req.Id,
  324. s.WorkHourDao.Columns.AttendeeId: 0,
  325. s.WorkHourDao.Columns.UserId: entity.OrganizerId,
  326. s.WorkHourDao.Columns.UserName: entity.OrganizerName,
  327. s.WorkHourDao.Columns.WorkDate: workDate,
  328. s.WorkHourDao.Columns.WorkHour: entity.Duration,
  329. s.WorkHourDao.Columns.Remark: entity.MeetingTitle,
  330. }
  331. service.SetCreatedInfo(orgWhData, s.GetCxtUserId(), s.GetCxtUserName())
  332. if _, err := s.WorkHourDao.TX(tx).Data(orgWhData).Insert(); err != nil {
  333. return myerrors.DbError("生成发起人工时失败")
  334. }
  335. }
  336. // 查询未生成工时的参会人员
  337. attendees, err := s.AttendeeDao.TX(tx).FieldsEx(s.AttendeeDao.Columns.DeletedTime).
  338. Where(s.AttendeeDao.Columns.MeetingId, req.Id).
  339. Where(s.AttendeeDao.Columns.WorkHourGenerated, 0).All()
  340. if err != nil {
  341. return myerrors.DbError("查询参会人员失败")
  342. }
  343. for _, row := range attendees {
  344. whData := g.Map{
  345. s.WorkHourDao.Columns.MeetingId: req.Id,
  346. s.WorkHourDao.Columns.AttendeeId: row.Id,
  347. s.WorkHourDao.Columns.UserId: row.UserId,
  348. s.WorkHourDao.Columns.UserName: row.UserName,
  349. s.WorkHourDao.Columns.WorkDate: workDate,
  350. s.WorkHourDao.Columns.WorkHour: entity.Duration,
  351. s.WorkHourDao.Columns.Remark: entity.MeetingTitle,
  352. }
  353. service.SetCreatedInfo(whData, s.GetCxtUserId(), s.GetCxtUserName())
  354. if _, err := s.WorkHourDao.TX(tx).Data(whData).Insert(); err != nil {
  355. return myerrors.DbError("生成参会人工时失败")
  356. }
  357. }
  358. // 标记参会人工时已生成
  359. if _, err := s.AttendeeDao.TX(tx).
  360. Data(g.Map{s.AttendeeDao.Columns.WorkHourGenerated: 1}).
  361. Where(s.AttendeeDao.Columns.MeetingId, req.Id).Update(); err != nil {
  362. return myerrors.DbError("更新参会人标记失败")
  363. }
  364. return nil
  365. })
  366. }
  367. // GetWorkHourList 获取会议工时记录列表
  368. func (s *PlatMeetingService) GetWorkHourList(meetingId int) ([]*opsdevmodel.PlatMeetingWorkHourRsp, error) {
  369. var entities []*opsdevmodel.PlatMeetingWorkHour
  370. err := s.WorkHourDao.FieldsEx(s.WorkHourDao.Columns.DeletedTime).
  371. Where(s.WorkHourDao.Columns.MeetingId, meetingId).
  372. Order(s.WorkHourDao.Columns.Id + " asc").Scan(&entities)
  373. if err != nil {
  374. return nil, myerrors.DbError("查询工时记录失败")
  375. }
  376. var list []*opsdevmodel.PlatMeetingWorkHourRsp
  377. if err := gconv.Structs(entities, &list); err != nil {
  378. return nil, myerrors.DbError("数据转换失败")
  379. }
  380. return list, nil
  381. }
  382. // getAttendeesByMeetingId 根据会议ID查询参会人员
  383. func (s *PlatMeetingService) getAttendeesByMeetingId(meetingId int) ([]*opsdevmodel.PlatMeetingAttendeeRsp, error) {
  384. var entities []*opsdevmodel.PlatMeetingAttendee
  385. err := s.AttendeeDao.FieldsEx(s.AttendeeDao.Columns.DeletedTime).
  386. Where(s.AttendeeDao.Columns.MeetingId, meetingId).
  387. Order(s.AttendeeDao.Columns.Id + " asc").Scan(&entities)
  388. if err != nil {
  389. return nil, myerrors.DbError("查询参会人员失败")
  390. }
  391. var list []*opsdevmodel.PlatMeetingAttendeeRsp
  392. if err := gconv.Structs(entities, &list); err != nil {
  393. return nil, myerrors.DbError("数据转换失败")
  394. }
  395. return list, nil
  396. }
  397. // batchInsertAttendees 批量新增参会人员(需在事务内调用)
  398. func (s *PlatMeetingService) batchInsertAttendees(
  399. tx *gdb.TX, meetingId int, userIds []int, userNames []string,
  400. ) error {
  401. for i, userId := range userIds {
  402. userName := ""
  403. if i < len(userNames) {
  404. userName = userNames[i]
  405. }
  406. data := g.Map{
  407. s.AttendeeDao.Columns.MeetingId: meetingId,
  408. s.AttendeeDao.Columns.UserId: userId,
  409. s.AttendeeDao.Columns.UserName: userName,
  410. }
  411. service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
  412. if _, err := s.AttendeeDao.TX(tx).Data(data).Insert(); err != nil {
  413. return myerrors.DbError("新增参会人员失败")
  414. }
  415. }
  416. return nil
  417. }