Files
zmkgC/internal/app/system/controller/notifications.go

490 lines
17 KiB
Go
Raw Normal View History

2025-07-07 20:11:59 +08:00
// ==========================================================================
// 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
}