package opsdev import ( "context" "dashoo.cn/opms_libary/myerrors" opsdevdao "dashoo.cn/opms_parent/app/dao/opsdev" opsdevmodel "dashoo.cn/opms_parent/app/model/opsdev" "dashoo.cn/opms_parent/app/service" "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" ) // PlatMeetingService 会议业务逻辑实现类 type PlatMeetingService struct { *service.ContextService MeetingDao *opsdevdao.PlatMeetingDao AttendeeDao *opsdevdao.PlatMeetingAttendeeDao WorkHourDao *opsdevdao.PlatMeetingWorkHourDao } // NewPlatMeetingService 初始化service func NewPlatMeetingService(ctx context.Context) (svc *PlatMeetingService, err error) { svc = new(PlatMeetingService) if svc.ContextService, err = svc.Init(ctx); err != nil { return nil, err } svc.MeetingDao = opsdevdao.NewPlatMeetingDao(svc.Tenant) svc.AttendeeDao = opsdevdao.NewPlatMeetingAttendeeDao(svc.Tenant) svc.WorkHourDao = opsdevdao.NewPlatMeetingWorkHourDao(svc.Tenant) return svc, nil } // GetList 分页查询会议列表 func (s *PlatMeetingService) GetList(req *opsdevmodel.PlatMeetingSearchReq) (total int, list []*opsdevmodel.PlatMeetingRsp, err error) { db := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime) if req.MeetingTitle != "" { db = db.Where(s.MeetingDao.Columns.MeetingTitle+" like ?", "%"+req.MeetingTitle+"%") } if req.OrganizerName != "" { db = db.Where(s.MeetingDao.Columns.OrganizerName+" like ?", "%"+req.OrganizerName+"%") } if req.DeptId > 0 { db = db.Where(s.MeetingDao.Columns.DeptId, req.DeptId) } if req.MeetingDateStart != "" { db = db.Where(s.MeetingDao.Columns.MeetingDate+" >= ?", req.MeetingDateStart) } if req.MeetingDateEnd != "" { db = db.Where(s.MeetingDao.Columns.MeetingDate+" <= ?", req.MeetingDateEnd) } total, err = db.Count() if err != nil { return 0, nil, myerrors.DbError("获取会议总数失败") } pageNum, pageSize := req.GetPage() db = db.Order(s.MeetingDao.Columns.CreatedTime + " desc") var entities []*opsdevmodel.PlatMeeting err = db.Page(pageNum, pageSize).Scan(&entities) if err != nil { return 0, nil, myerrors.DbError("查询会议列表失败") } if err = gconv.Structs(entities, &list); err != nil { return 0, nil, myerrors.DbError("数据转换失败") } // 查询每个会议的参会人员 for _, item := range list { attendees, _ := s.getAttendeesByMeetingId(int(item.Id)) item.Attendees = attendees } return } // GetById 根据ID获取会议详情 func (s *PlatMeetingService) GetById(id int) (*opsdevmodel.PlatMeetingRsp, error) { var entity opsdevmodel.PlatMeeting err := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime). WherePri(s.MeetingDao.Columns.Id, id).Scan(&entity) if err != nil { return nil, myerrors.DbError("查询会议失败") } if entity.Id <= 0 { return nil, myerrors.TipsError("会议不存在") } var rsp opsdevmodel.PlatMeetingRsp if err := gconv.Struct(entity, &rsp); err != nil { return nil, myerrors.DbError("数据转换失败") } attendees, err := s.getAttendeesByMeetingId(id) if err != nil { return nil, err } rsp.Attendees = attendees return &rsp, nil } // Create 新增会议(含参会人员 + 工时生成) func (s *PlatMeetingService) Create(req *opsdevmodel.PlatMeetingAddReq) error { data := g.Map{ s.MeetingDao.Columns.MeetingTitle: req.MeetingTitle, s.MeetingDao.Columns.MeetingContent: req.MeetingContent, s.MeetingDao.Columns.OrganizerId: req.OrganizerId, s.MeetingDao.Columns.OrganizerName: req.OrganizerName, s.MeetingDao.Columns.DeptId: req.DeptId, s.MeetingDao.Columns.Duration: req.Duration, s.MeetingDao.Columns.Remark: req.Remark, } if req.MeetingDate != "" { data[s.MeetingDao.Columns.MeetingDate] = req.MeetingDate } service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName()) return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { result, err := s.MeetingDao.TX(tx).Data(data).Insert() if err != nil { return myerrors.DbError("新增会议失败") } meetingId, err := result.LastInsertId() if err != nil { return myerrors.DbError("获取会议ID失败") } if err := s.batchInsertAttendees(tx, int(meetingId), req.UserIds, req.UserNames); err != nil { return err } // 生成发起人工时 workDate := gtime.Now() if req.MeetingDate != "" { workDate = gtime.NewFromStr(req.MeetingDate) } orgWhData := g.Map{ s.WorkHourDao.Columns.MeetingId: int(meetingId), s.WorkHourDao.Columns.AttendeeId: 0, s.WorkHourDao.Columns.UserId: req.OrganizerId, s.WorkHourDao.Columns.UserName: req.OrganizerName, s.WorkHourDao.Columns.WorkDate: workDate, s.WorkHourDao.Columns.WorkHour: req.Duration, s.WorkHourDao.Columns.Remark: req.MeetingTitle, } service.SetCreatedInfo(orgWhData, s.GetCxtUserId(), s.GetCxtUserName()) if _, err := s.WorkHourDao.TX(tx).Data(orgWhData).Insert(); err != nil { return myerrors.DbError("生成发起人工时失败") } // 生成参会人员工时 attendees, err := s.AttendeeDao.TX(tx).FieldsEx(s.AttendeeDao.Columns.DeletedTime). Where(s.AttendeeDao.Columns.MeetingId, int(meetingId)).All() if err != nil { return myerrors.DbError("查询参会人员失败") } for _, row := range attendees { whData := g.Map{ s.WorkHourDao.Columns.MeetingId: int(meetingId), s.WorkHourDao.Columns.AttendeeId: row.Id, s.WorkHourDao.Columns.UserId: row.UserId, s.WorkHourDao.Columns.UserName: row.UserName, s.WorkHourDao.Columns.WorkDate: workDate, s.WorkHourDao.Columns.WorkHour: req.Duration, s.WorkHourDao.Columns.Remark: req.MeetingTitle, } service.SetCreatedInfo(whData, s.GetCxtUserId(), s.GetCxtUserName()) if _, err := s.WorkHourDao.TX(tx).Data(whData).Insert(); err != nil { return myerrors.DbError("生成参会人工时失败") } } // 标记参会人工时已生成 if _, err := s.AttendeeDao.TX(tx). Data(g.Map{s.AttendeeDao.Columns.WorkHourGenerated: 1}). Where(s.AttendeeDao.Columns.MeetingId, int(meetingId)).Update(); err != nil { return myerrors.DbError("更新参会人标记失败") } return nil }) } // UpdateById 更新会议 func (s *PlatMeetingService) UpdateById(req *opsdevmodel.PlatMeetingUpdateReq) error { var entity opsdevmodel.PlatMeeting err := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime). WherePri(s.MeetingDao.Columns.Id, req.Id).Scan(&entity) if err != nil { return myerrors.DbError("查询会议失败") } if entity.Id <= 0 { return myerrors.TipsError("会议不存在") } data := g.Map{} if req.MeetingTitle != "" { data[s.MeetingDao.Columns.MeetingTitle] = req.MeetingTitle } if req.MeetingContent != "" { data[s.MeetingDao.Columns.MeetingContent] = req.MeetingContent } if req.MeetingDate != "" { data[s.MeetingDao.Columns.MeetingDate] = req.MeetingDate } if req.Duration > 0 { data[s.MeetingDao.Columns.Duration] = req.Duration } if req.OrganizerId > 0 { data[s.MeetingDao.Columns.OrganizerId] = req.OrganizerId } if req.OrganizerName != "" { data[s.MeetingDao.Columns.OrganizerName] = req.OrganizerName } if req.DeptId > 0 { data[s.MeetingDao.Columns.DeptId] = req.DeptId } if req.Remark != "" { data[s.MeetingDao.Columns.Remark] = req.Remark } if len(data) == 0 && len(req.UserIds) == 0 { return nil } service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName()) return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { if len(data) > 0 { _, err = s.MeetingDao.TX(tx).FieldsEx(service.UpdateFieldEx...). Data(data).WherePri(s.MeetingDao.Columns.Id, req.Id).Update() if err != nil { return myerrors.DbError("更新会议失败") } } // 全量替换参会人员 if len(req.UserIds) > 0 { if _, err := s.AttendeeDao.TX(tx). Where(s.AttendeeDao.Columns.MeetingId, req.Id).Delete(); err != nil { return myerrors.DbError("清除旧参会人员失败") } if err := s.batchInsertAttendees(tx, req.Id, req.UserIds, req.UserNames); err != nil { return err } } return nil }) } // DeleteByIds 删除会议(软删除会议及参会人员) func (s *PlatMeetingService) DeleteByIds(ids []int64) error { if len(ids) == 0 { return myerrors.TipsError("请选择需要删除的记录") } return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { // 软删除参会人员 for _, id := range ids { if _, err := s.AttendeeDao.TX(tx). Where(s.AttendeeDao.Columns.MeetingId, id).Delete(); err != nil { return myerrors.DbError("删除参会人员失败") } } // 软删除会议 if _, err := s.MeetingDao.TX(tx).WhereIn(s.MeetingDao.Columns.Id, ids).Delete(); err != nil { return myerrors.DbError("删除会议失败") } return nil }) } // AddAttendees 追加参会人员 func (s *PlatMeetingService) AddAttendees(req *opsdevmodel.PlatMeetingAddAttendeeReq) error { // 校验会议存在 var entity opsdevmodel.PlatMeeting err := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime). WherePri(s.MeetingDao.Columns.Id, req.MeetingId).Scan(&entity) if err != nil { return myerrors.DbError("查询会议失败") } if entity.Id <= 0 { return myerrors.TipsError("会议不存在") } return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { // 查询已存在参会人,避免重复插入 existing, err := s.AttendeeDao.TX(tx). Where(s.AttendeeDao.Columns.MeetingId, req.MeetingId). WhereIn(s.AttendeeDao.Columns.UserId, req.UserIds). Fields(s.AttendeeDao.Columns.UserId).All() if err != nil { return myerrors.DbError("查询已有参会人员失败") } existingIds := make(map[int]bool) for _, row := range existing { existingIds[row["user_id"].Int()] = true } for i, userId := range req.UserIds { if existingIds[userId] { continue } userName := "" if i < len(req.UserNames) { userName = req.UserNames[i] } attendeeData := g.Map{ s.AttendeeDao.Columns.MeetingId: req.MeetingId, s.AttendeeDao.Columns.UserId: userId, s.AttendeeDao.Columns.UserName: userName, } service.SetCreatedInfo(attendeeData, s.GetCxtUserId(), s.GetCxtUserName()) if _, err := s.AttendeeDao.TX(tx).Data(attendeeData).Insert(); err != nil { return myerrors.DbError("新增参会人员失败") } } return nil }) } // Complete 结束会议——为发起人和参会人员补生成工时 func (s *PlatMeetingService) Complete(req *opsdevmodel.PlatMeetingCompleteReq) error { var entity opsdevmodel.PlatMeeting err := s.MeetingDao.FieldsEx(s.MeetingDao.Columns.DeletedTime). WherePri(s.MeetingDao.Columns.Id, req.Id).Scan(&entity) if err != nil { return myerrors.DbError("查询会议失败") } if entity.Id <= 0 { return myerrors.TipsError("会议不存在") } if entity.Duration <= 0 { return myerrors.TipsError("会议时长未设置,无法生成工时") } return s.MeetingDao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { workDate := gtime.Now() if entity.MeetingDate != nil { workDate = entity.MeetingDate } // 检查发起人工时是否已存在 orgCount, err := s.WorkHourDao.TX(tx).FieldsEx(s.WorkHourDao.Columns.DeletedTime). Where(s.WorkHourDao.Columns.MeetingId, req.Id). Where(s.WorkHourDao.Columns.UserId, entity.OrganizerId). Where(s.WorkHourDao.Columns.AttendeeId, 0).Count() if err != nil { return myerrors.DbError("查询发起人工时失败") } if orgCount == 0 { orgWhData := g.Map{ s.WorkHourDao.Columns.MeetingId: req.Id, s.WorkHourDao.Columns.AttendeeId: 0, s.WorkHourDao.Columns.UserId: entity.OrganizerId, s.WorkHourDao.Columns.UserName: entity.OrganizerName, s.WorkHourDao.Columns.WorkDate: workDate, s.WorkHourDao.Columns.WorkHour: entity.Duration, s.WorkHourDao.Columns.Remark: entity.MeetingTitle, } service.SetCreatedInfo(orgWhData, s.GetCxtUserId(), s.GetCxtUserName()) if _, err := s.WorkHourDao.TX(tx).Data(orgWhData).Insert(); err != nil { return myerrors.DbError("生成发起人工时失败") } } // 查询未生成工时的参会人员 attendees, err := s.AttendeeDao.TX(tx).FieldsEx(s.AttendeeDao.Columns.DeletedTime). Where(s.AttendeeDao.Columns.MeetingId, req.Id). Where(s.AttendeeDao.Columns.WorkHourGenerated, 0).All() if err != nil { return myerrors.DbError("查询参会人员失败") } for _, row := range attendees { whData := g.Map{ s.WorkHourDao.Columns.MeetingId: req.Id, s.WorkHourDao.Columns.AttendeeId: row.Id, s.WorkHourDao.Columns.UserId: row.UserId, s.WorkHourDao.Columns.UserName: row.UserName, s.WorkHourDao.Columns.WorkDate: workDate, s.WorkHourDao.Columns.WorkHour: entity.Duration, s.WorkHourDao.Columns.Remark: entity.MeetingTitle, } service.SetCreatedInfo(whData, s.GetCxtUserId(), s.GetCxtUserName()) if _, err := s.WorkHourDao.TX(tx).Data(whData).Insert(); err != nil { return myerrors.DbError("生成参会人工时失败") } } // 标记参会人工时已生成 if _, err := s.AttendeeDao.TX(tx). Data(g.Map{s.AttendeeDao.Columns.WorkHourGenerated: 1}). Where(s.AttendeeDao.Columns.MeetingId, req.Id).Update(); err != nil { return myerrors.DbError("更新参会人标记失败") } return nil }) } // GetWorkHourList 获取会议工时记录列表 func (s *PlatMeetingService) GetWorkHourList(meetingId int) ([]*opsdevmodel.PlatMeetingWorkHourRsp, error) { var entities []*opsdevmodel.PlatMeetingWorkHour err := s.WorkHourDao.FieldsEx(s.WorkHourDao.Columns.DeletedTime). Where(s.WorkHourDao.Columns.MeetingId, meetingId). Order(s.WorkHourDao.Columns.Id + " asc").Scan(&entities) if err != nil { return nil, myerrors.DbError("查询工时记录失败") } var list []*opsdevmodel.PlatMeetingWorkHourRsp if err := gconv.Structs(entities, &list); err != nil { return nil, myerrors.DbError("数据转换失败") } return list, nil } // getAttendeesByMeetingId 根据会议ID查询参会人员 func (s *PlatMeetingService) getAttendeesByMeetingId(meetingId int) ([]*opsdevmodel.PlatMeetingAttendeeRsp, error) { var entities []*opsdevmodel.PlatMeetingAttendee err := s.AttendeeDao.FieldsEx(s.AttendeeDao.Columns.DeletedTime). Where(s.AttendeeDao.Columns.MeetingId, meetingId). Order(s.AttendeeDao.Columns.Id + " asc").Scan(&entities) if err != nil { return nil, myerrors.DbError("查询参会人员失败") } var list []*opsdevmodel.PlatMeetingAttendeeRsp if err := gconv.Structs(entities, &list); err != nil { return nil, myerrors.DbError("数据转换失败") } return list, nil } // batchInsertAttendees 批量新增参会人员(需在事务内调用) func (s *PlatMeetingService) batchInsertAttendees( tx *gdb.TX, meetingId int, userIds []int, userNames []string, ) error { for i, userId := range userIds { userName := "" if i < len(userNames) { userName = userNames[i] } data := g.Map{ s.AttendeeDao.Columns.MeetingId: meetingId, s.AttendeeDao.Columns.UserId: userId, s.AttendeeDao.Columns.UserName: userName, } service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName()) if _, err := s.AttendeeDao.TX(tx).Data(data).Insert(); err != nil { return myerrors.DbError("新增参会人员失败") } } return nil }