// ========================================================================== // GFast自动生成controller操作代码。 // 生成日期:2024-04-11 16:56:36 // 生成路径: internal/app/system/controller/notifications.go // 生成人:gfast // desc:通知信息 // company:云南奇讯科技有限公司 // ========================================================================== package controller import ( "context" "fmt" "strings" "time" "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" "github.com/jinzhu/copier" "github.com/samber/lo" "github.com/tiger1103/gfast/v3/api/v1/common/coryCommon" "github.com/tiger1103/gfast/v3/api/v1/system" comModel "github.com/tiger1103/gfast/v3/internal/app/common/model" "github.com/tiger1103/gfast/v3/internal/app/system/dao" ct "github.com/tiger1103/gfast/v3/internal/app/system/logic/context" "github.com/tiger1103/gfast/v3/internal/app/system/model" "github.com/tiger1103/gfast/v3/internal/app/system/model/do" "github.com/tiger1103/gfast/v3/internal/app/system/model/entity" "github.com/tiger1103/gfast/v3/internal/app/system/service" wxDao "github.com/tiger1103/gfast/v3/internal/app/wxApplet/dao" "github.com/tiger1103/gfast/v3/library/liberr" richtext "github.com/tiger1103/gfast/v3/third/richText" ) type notificationsController struct { BaseController } var Notifications = new(notificationsController) // List 列表 func (c *notificationsController) List(ctx context.Context, req *system.NotificationsSearchReq) (res *system.NotificationsSearchRes, err error) { res, err = service.Notifications().List(ctx, req) return } // Get 获取通知信息 func (c *notificationsController) Get(ctx context.Context, req *system.NotificationsGetReq) (res *system.NotificationsGetRes, err error) { res = new(system.NotificationsGetRes) res.NotificationsInfoRes, err = service.Notifications().GetById(ctx, req.Id) return } // Add 添加通知信息 func (c *notificationsController) Add(ctx context.Context, req *system.NotificationsAddReq) (res *system.NotificationsAddRes, err error) { err = service.Notifications().Add(ctx, req) return } // Edit 修改通知信息 func (c *notificationsController) Edit(ctx context.Context, req *system.NotificationsEditReq) (res *system.NotificationsEditRes, err error) { err = service.Notifications().Edit(ctx, req) return } // Delete 删除通知信息 func (c *notificationsController) Delete(ctx context.Context, req *system.NotificationsDeleteReq) (res *system.NotificationsDeleteRes, err error) { err = service.Notifications().Delete(ctx, req.Ids) return } // NotificationsPublishReq 发布通知请求参数 func (c *notificationsController) Publish(ctx context.Context, req *system.NotificationsPublishReq) (*system.NotificationsPublishRes, error) { // 获取当前用户的 ID userID := ct.New().GetUserId(ctx) err := PublicPublish(ctx, req, userID) liberr.ErrIsNil(ctx, err) return &system.NotificationsPublishRes{}, nil } // NotificationsGetUserReq 获取当前用户的通知信息 func (c *notificationsController) GetNotify(ctx context.Context, req *system.NotificationsGetUserReq) (res *system.NotificationsGetUserRes, err error) { res = new(system.NotificationsGetUserRes) // 获取当前用户的 ID userID := ct.New().GetUserId(ctx) // 当前用户的通知列表 notification := []model.Notifications{} err = g.Try(ctx, func(ctx context.Context) { // 获取当前用户的通知信息 m := dao.NotificationRecipients.Ctx(ctx).As("nf"). InnerJoin("notifications as ns", "ns.id = notification_id"). LeftJoin("sys_user as su", "ns.initiator = su.id"). Where("nf.recipient_id", userID). //Where("nf.notification_status", 0). Where("ns.is_app", req.IsApp). Where("ns.project_id", req.ProjectId) if req.PageNum == 0 { req.PageNum = 1 } if req.PageSize == 0 { req.PageSize = 10 } // 获取当前页的数据 res.Total, err = m.Count() liberr.ErrIsNil(ctx, err) // 更新当前页 res.CurrentPage = req.PageNum err = m.Page(req.PageNum, req.PageSize).Fields("ns.route,ns.notification_text,ns.notification_time,nf.notification_status,nf.id,ns.title,ns.files,su.user_nickname"). OrderDesc("ns.id"). Scan(¬ification) liberr.ErrIsNil(ctx, err) }) // 初始化通知列表 res.List = make([]system.Notifications, 0, len(notification)) copier.CopyWithOption(&res.List, ¬ification, copier.Option{ Converters: []copier.TypeConverter{ { SrcType: copier.String, DstType: []string{}, Fn: func(src interface{}) (dst interface{}, err error) { s, ok := src.(string) if !ok { return nil, fmt.Errorf("转换失败") } if s == "" { return []string{}, nil } return lo.Compact(strings.Split(s, "|")), nil }, }, }, }) return res, nil } // NotificationsReadReq 标记通知为已读 func (c *notificationsController) Read(ctx context.Context, req *system.NotificationsReadReq) (*system.NotificationsReadRes, error) { // 获取当前用户的 ID userID := ct.New().GetUserId(ctx) err := g.Try(ctx, func(ctx context.Context) { // select * // from notification_recipients nr // join notifications ns on nr.notification_id = ns.id // where nr.recipient_id = 1 and ns.project_id = 37 and is_app = 0 and nr.notification_status = 0; _, err := dao.NotificationRecipients.Ctx(ctx).As("nr"). InnerJoin("notifications as ns", "nr.notification_id = ns.id"). Where("nr.recipient_id", userID). Where("ns.project_id", req.ProjectId). Where("ns.is_app", 0). Where("nr.notification_status", 0). Data(g.Map{ dao.NotificationRecipients.Columns().NotificationStatus: 1, dao.NotificationRecipients.Columns().ReadTime: time.Now().Format("2006-01-02 15:04:05"), }).Update() liberr.ErrIsNil(ctx, err) }) liberr.ErrIsNil(ctx, err) return &system.NotificationsReadRes{}, nil } // 查看通知公告详情 func (c *notificationsController) Detail(ctx context.Context, req *system.NotificationsDetailReq) (*system.NotificationsDetailRes, error) { // 获取当前用户的 ID currentUserID := ct.New().GetUserId(ctx) // 初始化 NotificationsDetail notificationDetails := system.NotificationsDetail{} // 获取通知详情 notificationInfo := model.Notifications{} err := g.Try(ctx, func(ctx context.Context) { err := dao.NotificationRecipients.Ctx(ctx).As("nf"). InnerJoin("notifications as ns", "ns.id = nf.notification_id"). LeftJoin("sys_user as su", "ns.initiator = su.id"). Where("nf.id", req.Id). Fields("ns.route,ns.notification_text,ns.notification_time,nf.notification_status,ns.id,ns.title,su.user_nickname"). Scan(¬ificationInfo) liberr.ErrIsNil(ctx, err) // SELECT * // FROM notification_files // WHERE notification_id = ( // SELECT nr.notification_id // FROM notification_files AS file // LEFT JOIN notification_recipients AS nr ON nr.notification_id = file.notification_id // WHERE nr.id = 775 // LIMIT 1 // ); // 获取对应的文件 dao.NotificationFiles.Ctx(ctx).Where("notification_id = ?", dao.NotificationFiles.Ctx(ctx).As("file"). LeftJoin("notification_recipients AS nr", "nr.notification_id = file.notification_id"). Where("nr.id", req.Id).Fields("nr.notification_id").Limit(1)). Fields("file_name, file_path, file_type, file_size").Scan(¬ificationInfo.Files) // 更新通知状态为已读 _, err = dao.NotificationRecipients.Ctx(ctx). Where(dao.NotificationRecipients.Columns().RecipientId, currentUserID). Where(dao.NotificationRecipients.Columns().Id, req.Id). Where(dao.NotificationRecipients.Columns().NotificationStatus, 0). Data(g.Map{ dao.NotificationRecipients.Columns().NotificationStatus: 1, dao.NotificationRecipients.Columns().ReadTime: time.Now().Format("2006-01-02 15:04:05"), }). Update() liberr.ErrIsNil(ctx, err) key := "notification:" + notificationInfo.Id exist, _ := g.Redis().Exists(ctx, key) if exist == 0 { g.Redis().Set(ctx, key, 1) } else { g.Redis().IncrBy(ctx, key, 1) } }) liberr.ErrIsNil(ctx, err) copier.Copy(¬ificationDetails, ¬ificationInfo) return &system.NotificationsDetailRes{ List: notificationDetails, }, nil } // NotificationsGetPeople 获取通知公告的未读人员或已读人员 func (c *notificationsController) GetPeople(ctx context.Context, req *system.NotificationsGetPeopleReq) (*system.NotificationsGetPeopleRes, error) { // 初始化 NotificationsGetPeopleRes notificationGetPeople := system.NotificationsGetPeopleRes{} err := g.Try(ctx, func(ctx context.Context) { var err error // 根据通知ID获取通知详情 m := dao.NotificationRecipients.Ctx(ctx).As("nr"). Where("nr.notification_id = ?", dao.NotificationRecipients.Ctx(ctx). Where(dao.NotificationRecipients.Columns().Id, req.Id). Fields(dao.NotificationRecipients.Columns().NotificationId)). Where("nr.notification_status", req.Status) // 分页 if req.PageNum == 0 { req.PageNum = 1 } if req.PageSize == 0 { req.PageSize = 20 } // 更新总数量和当前页 notificationGetPeople.Total, err = m.Fields("DISTINCT nr.id").Count() notificationGetPeople.CurrentPage = req.PageNum // 用户信息 notificationInfo := []model.NotificationsGetRes{} // 获取数据 err = m.Page(req.PageNum, req.PageSize). InnerJoin("sys_user as su", "nr.recipient_id = su.id"). Fields("nr.recipient_id,su.user_nickname,nr.read_time,su.mobile"). Scan(¬ificationInfo) liberr.ErrIsNil(ctx, err) // 根据用户 ID 去重 notificationInfo = lo.UniqBy(notificationInfo, func(item model.NotificationsGetRes) int { return int(item.Id) }) // 获取所有的用户的手机号 userMobiles := lo.FilterMap(notificationInfo, func(item model.NotificationsGetRes, _ int) (string, bool) { if item.OrmMobile == "" { // 如果手机号为空则跳过 return "", false } return item.OrmMobile, true }) // 根据手机号在 bus_construction_user 表中获取用户的信息 userInfoList := make([]entity.BusConstructionUser, 0, len(userMobiles)) err = dao.BusConstructionUser.Ctx(ctx).WhereIn(dao.BusConstructionUser.Columns().Phone, userMobiles). Fields("phone,head_icon,pace_photo,user_name").Scan(&userInfoList) liberr.ErrIsNil(ctx, err) // Slice To Map // Key 为手机号,Value 为用户信息 userMap := lo.Associate(userInfoList, func(user entity.BusConstructionUser) (string, entity.BusConstructionUser) { return user.Phone, user }) for i, user := range notificationInfo { if cUser, ok := userMap[user.OrmMobile]; ok { // 如果小程序中存在与PC端用户相同的手机号 // 如果在 "PC端" 中没有设置用户名 if notificationInfo[i].Username == "" { // 优先使用小程序的真实姓名 notificationInfo[i].Username = cUser.UserName if cUser.UserName == "" { notificationInfo[i].Username = cUser.NickName // 小程序的昵称作为备选 } } var facePhoto string if cUser.PacePhoto != "" { facePhoto = cUser.PacePhoto // 优先使用实名人脸照 } else if strings.Contains(cUser.HeadIcon, "data") { // 如果 Head_icon 是 Base64 编码的图片 // 则将其转换为图片后保存到本地并更新数据库 newHeadIcon, err := coryCommon.Base64ToImgFunc(cUser.HeadIcon, "1", coryCommon.Helmet) if err == nil { // 更新数据库仅当转换成功时 _, err = dao.BusConstructionUser.Ctx(ctx).Where(dao.BusConstructionUser.Columns().Phone, cUser.Phone).Data(g.Map{ dao.BusConstructionUser.Columns().HeadIcon: newHeadIcon, }).Update() facePhoto = newHeadIcon } } else { facePhoto = cUser.HeadIcon // Head_icon 登陆照片作为备选 } // 设置 FacePhoto notificationInfo[i].FacePhoto = facePhoto } } copier.Copy(¬ificationGetPeople.List, ¬ificationInfo) }) liberr.ErrIsNil(ctx, err) return ¬ificationGetPeople, nil } func PublicPublish(ctx context.Context, req *system.NotificationsPublishReq, userID uint64) error { err := g.Try(ctx, func(ctx context.Context) { var ( // 当前时间 timeNow = time.Now().Format("2006-01-02 15:04:05") // 岗位名 positions = strings.Builder{} ) // PC 通知消息 // 按岗位发布通知 if len(req.Positions) > 0 { // 获取岗位下的所有用户 userIDs, err := dao.SysUserPost.Ctx(ctx).Where("post_id in (?)", req.Positions).Fields(dao.SysUserPost.Columns().UserId).Distinct().Array() req.Users = lo.Map(userIDs, func(item *gvar.Var, _ int) int { return item.Int() }) // 获取岗位对应的岗位名称 postNames, err := dao.SysPost.Ctx(ctx).Where("post_id in (?)", req.Positions).Fields(dao.SysPost.Columns().PostName).Distinct().Array() // 拼接岗位名 lo.ForEach(postNames, func(position *gvar.Var, _ int) { positions.WriteString(position.String()) positions.WriteString(",") }) liberr.ErrIsNil(ctx, err) } // App 通知 // 如果 Users 和 Positions 都为空则通知该项目下的所有人 if len(req.Users) == 0 && len(req.Positions) == 0 { // 获取项目下的所有用户 projectUserIDs, err := wxDao.SysUserProjectRelevancy.Ctx(ctx). Where(wxDao.SysUserProjectRelevancy.Columns().ProjectId, req.ProjectId). Fields(wxDao.SysUserProjectRelevancy.Columns().UserId).Distinct().Array() liberr.ErrIsNil(ctx, err) constructionUserIDs, err := dao.BusConstructionUser.Ctx(ctx).As("bcs"). InnerJoin("sys_user as su", "bcs.phone = su.mobile"). Where("bcs.project_id", req.ProjectId). Fields("su.id").Distinct().Array() liberr.ErrIsNil(ctx, err) // 将两个用户ID列表合并并去重 req.Users = lo.Uniq(lo.Map(append(projectUserIDs, constructionUserIDs...), func(item *gvar.Var, _ int) int { return item.Int() })) } // 在 Notifications 表中插入本次通知的数据 notification := do.Notifications{ Title: req.NotificationTitle, // 通知标题 NotificationText: req.NotificationText, // 通知正文 Route: req.Route, // 跳转的路由 NotificationTime: timeNow, // 通知时间 Initiator: userID, // 发起人 ProjectId: req.ProjectId, // 项目ID Positions: positions.String(), // 岗位名称 IntroduceId: req.ProjectNewsId, // 关联的 PC 端项目新闻ID } // 如果是来自 App 的发布通知 if req.IsApp != 0 { notification.IsApp = req.IsApp // 修改通知类型 } // 如果是来自 App 的项目新闻 // 需要同步一份给 PC 端 if req.IsApp <= 2 && req.IsApp >= 1 { // 同步的项目新闻 notificationText := req.NotificationText // 如果 req.file 不为空,则将其包装为富文本标签 if len(req.Files) > 0 { images := lo.Map(req.Files, func(file *comModel.UpFile, _ int) string { return file.Url }) // 将图片链接转换为富文本标签后附加到通知正文中 notificationText += richtext.ConvertImageURLsToRichText(images) } // 为后台插入一份项目新闻 introduceID, err := dao.SysProjectIntroduce.Ctx(ctx).Data(entity.SysProjectIntroduce{ ProjectId: int64(req.ProjectId), Headline: req.NotificationTitle, RichText: notificationText, CreatedBy: gconv.String(userID), }).InsertAndGetId() liberr.ErrIsNil(ctx, err) // App 关联的项目新闻ID并移除富文本 notification.IntroduceId = introduceID notification.NotificationText = richtext.RemoveRichText(req.NotificationText) } // 插入数据库后返回其`主键ID` lastInsertID, err := dao.Notifications.Ctx(ctx).InsertAndGetId(notification) liberr.ErrIsNil(ctx, err) // 如果不存在接收人则返回错误 if len(req.Users) == 0 { liberr.ErrIsNil(ctx, fmt.Errorf("接收人不能为空")) } // 如果是 App 通知包含附件的的情况下 if len(req.Files) > 0 { if err := saveNotificationFiles(ctx, lastInsertID, req.Files); err != nil { liberr.ErrIsNil(ctx, err) } } // 在 notification_recipients 表中为每个接收者插入一条数据 var notificationsList []do.NotificationRecipients lo.ForEach(req.Users, func(item int, _ int) { notificationsList = append(notificationsList, do.NotificationRecipients{ NotificationId: lastInsertID, // 通知ID RecipientId: item, // 接收者ID NotificationStatus: 0, // 0 未读 IntroduceId: req.ProjectNewsId, // 关联的PC 端项目新闻ID }) }) // 批量插入 _, err = dao.NotificationRecipients.Ctx(ctx).Insert(notificationsList) liberr.ErrIsNil(ctx, err) }) return err } // saveNotificationFiles 将上传的文件的信息保存到数据库 func saveNotificationFiles(ctx context.Context, notificationID int64, files []*comModel.UpFile) error { notificationFileList := make([]do.NotificationFiles, 0, len(files)) for i := 0; i < len(files); i++ { notificationFileList = append(notificationFileList, do.NotificationFiles{ NotificationId: notificationID, FileName: files[i].Name, FilePath: files[i].Url, FileType: files[i].FileType, FileSize: files[i].Size, }) } _, err := dao.NotificationFiles.Ctx(ctx).Insert(notificationFileList) return err }