Files
zmkgC/internal/app/system/controller/notifications.go
2025-07-07 20:11:59 +08:00

490 lines
17 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ==========================================================================
// 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(&notification)
liberr.ErrIsNil(ctx, err)
})
// 初始化通知列表
res.List = make([]system.Notifications, 0, len(notification))
copier.CopyWithOption(&res.List, &notification, 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(&notificationInfo)
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(&notificationInfo.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(&notificationDetails, &notificationInfo)
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(&notificationInfo)
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(&notificationGetPeople.List, &notificationInfo)
})
liberr.ErrIsNil(ctx, err)
return &notificationGetPeople, 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
}