yunzerwebsiteallinone/go/services/system_sitereminder.go
2026-06-17 23:07:39 +08:00

400 lines
12 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.

package services
import (
"context"
"fmt"
"time"
"github.com/beego/beego/v2/client/orm"
"server/models"
)
// GetSiteReminderConfig 获取站内信配置(只读首条记录,不存在则初始化默认值)
func GetSiteReminderConfig() (models.SystemSiteReminder, error) {
var row models.SystemSiteReminder
err := models.Orm.QueryTable(new(models.SystemSiteReminder)).OrderBy("id").Limit(1).One(&row)
if err == orm.ErrNoRows {
// 默认配置
now := time.Now()
row = models.SystemSiteReminder{
RetentionDays: 30,
AutoRead: 0,
CreateTime: &now,
UpdateTime: &now,
}
_, err = models.Orm.Insert(&row)
if err != nil {
return row, err
}
return row, nil
}
return row, err
}
// SaveSiteReminderConfig 保存/更新配置
func SaveSiteReminderConfig(retentionDays int, autoRead int8) error {
if retentionDays <= 0 {
retentionDays = 30
}
cfg, err := GetSiteReminderConfig()
if err != nil {
return err
}
now := time.Now()
cfg.RetentionDays = retentionDays
cfg.AutoRead = autoRead
cfg.UpdateTime = &now
_, err = models.Orm.Update(&cfg, "RetentionDays", "AutoRead", "UpdateTime")
return err
}
// SendSiteReminder 发送站内信
// targetType: platform (平台端), tenant_all (管理端所有用户), role (平台角色), tenant (特定租户)
func SendSiteReminder(title, content string, senderID uint64, senderType string, targetType string, targetRoleID uint64, targetTenantID uint64) error {
var receiverIDs []uint64
var receiverType string
switch targetType {
case "platform":
receiverType = "platform"
var list []models.AdminUser
_, err := models.Orm.QueryTable(new(models.AdminUser)).Filter("status", 1).Filter("delete_time__isnull", true).All(&list, "id")
if err != nil {
return fmt.Errorf("查询平台用户失败: %w", err)
}
for _, u := range list {
receiverIDs = append(receiverIDs, u.ID)
}
case "tenant_all":
receiverType = "tenant"
var list []models.SystemTenantUser
_, err := models.Orm.QueryTable(new(models.SystemTenantUser)).Filter("status", 1).Filter("delete_time__isnull", true).All(&list, "id")
if err != nil {
return fmt.Errorf("查询租户用户失败: %w", err)
}
for _, u := range list {
receiverIDs = append(receiverIDs, u.ID)
}
case "role":
receiverType = "platform"
var list []models.AdminUser
_, err := models.Orm.QueryTable(new(models.AdminUser)).Filter("status", 1).Filter("role_id", targetRoleID).Filter("delete_time__isnull", true).All(&list, "id")
if err != nil {
return fmt.Errorf("根据角色查询用户失败: %w", err)
}
for _, u := range list {
receiverIDs = append(receiverIDs, u.ID)
}
case "tenant":
receiverType = "tenant"
var list []models.SystemTenantUser
_, err := models.Orm.QueryTable(new(models.SystemTenantUser)).Filter("status", 1).Filter("tid", targetTenantID).Filter("delete_time__isnull", true).All(&list, "id")
if err != nil {
return fmt.Errorf("根据租户查询用户失败: %w", err)
}
for _, u := range list {
receiverIDs = append(receiverIDs, u.ID)
}
default:
return fmt.Errorf("未知的发送目标类型: %s", targetType)
}
if len(receiverIDs) == 0 {
return nil
}
now := time.Now()
batchID := fmt.Sprintf("%d_%d", now.UnixNano(), senderID)
var reminders []models.SystemReminderList
for _, rid := range receiverIDs {
reminders = append(reminders, models.SystemReminderList{
Title: title,
Content: content,
SenderID: senderID,
SenderType: senderType,
ReceiverID: rid,
ReceiverType: receiverType,
IsRead: 0,
CreateTime: &now,
BatchID: batchID,
TargetType: targetType,
TargetRoleID: targetRoleID,
TargetTenantID: targetTenantID,
})
}
// 批量插入
_, err := models.Orm.InsertMulti(100, reminders)
return err
}
// ListReminders 列表查询
func ListReminders(receiverID uint64, receiverType string, page, pageSize int, isRead *int8) ([]models.SystemReminderList, int64, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 {
pageSize = 10
}
var list []models.SystemReminderList
qs := models.Orm.QueryTable(new(models.SystemReminderList)).
Filter("receiver_id", receiverID).
Filter("receiver_type", receiverType).
Filter("delete_time__isnull", true)
if isRead != nil {
qs = qs.Filter("is_read", *isRead)
}
total, err := qs.Count()
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
_, err = qs.OrderBy("-create_time", "-id").Limit(pageSize, offset).All(&list)
return list, total, err
}
// MarkReminderRead 标记单条已读
func MarkReminderRead(id uint64, receiverID uint64, receiverType string) error {
now := time.Now()
_, err := models.Orm.QueryTable(new(models.SystemReminderList)).
Filter("id", id).
Filter("receiver_id", receiverID).
Filter("receiver_type", receiverType).
Update(map[string]interface{}{
"is_read": 1,
"read_time": &now,
})
return err
}
// MarkAllRemindersRead 一键全部已读
func MarkAllRemindersRead(receiverID uint64, receiverType string) error {
now := time.Now()
_, err := models.Orm.QueryTable(new(models.SystemReminderList)).
Filter("receiver_id", receiverID).
Filter("receiver_type", receiverType).
Filter("is_read", 0).
Update(map[string]interface{}{
"is_read": 1,
"read_time": &now,
})
return err
}
// DeleteReminder 删除消息
func DeleteReminder(id uint64, receiverID uint64, receiverType string) error {
now := time.Now()
_, err := models.Orm.QueryTable(new(models.SystemReminderList)).
Filter("id", id).
Filter("receiver_id", receiverID).
Filter("receiver_type", receiverType).
Update(map[string]interface{}{
"delete_time": &now,
})
return err
}
// AutoCleanExpiredReminders 自动清理过期站内信
func AutoCleanExpiredReminders() error {
cfg, err := GetSiteReminderConfig()
if err != nil {
return err
}
if cfg.RetentionDays <= 0 {
return nil
}
expireTime := time.Now().AddDate(0, 0, -cfg.RetentionDays)
_, err = models.Orm.QueryTable(new(models.SystemReminderList)).
Filter("create_time__lt", expireTime).
Delete()
return err
}
// ListSentReminders 获取已发送的消息列表(按 batch_id 分组)
func ListSentReminders(senderID uint64, page, pageSize int) ([]models.SystemReminderList, int64, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 {
pageSize = 10
}
offset := (page - 1) * pageSize
var total int64
err := models.Orm.Raw("SELECT COUNT(DISTINCT batch_id) FROM yz_system_reminderlist WHERE sender_id = ? AND delete_time IS NULL", senderID).QueryRow(&total)
if err != nil {
return nil, 0, err
}
var list []models.SystemReminderList
_, err = models.Orm.Raw("SELECT * FROM yz_system_reminderlist WHERE id IN (SELECT MIN(id) FROM yz_system_reminderlist WHERE sender_id = ? AND delete_time IS NULL GROUP BY batch_id) ORDER BY id DESC LIMIT ? OFFSET ?", senderID, pageSize, offset).QueryRows(&list)
if err != nil {
return nil, 0, err
}
return list, total, nil
}
// UpdateSentReminder 更新已发出的消息(更新该批次下所有接收者的消息,支持修改目标接收群体)
func UpdateSentReminder(batchID string, title, content, targetType string, targetRoleID, targetTenantID uint64) error {
// 1. 获取当前发送者ID (从该批次中任意一条记录中获取)
var firstRecord models.SystemReminderList
err := models.Orm.QueryTable(new(models.SystemReminderList)).Filter("batch_id", batchID).Limit(1).One(&firstRecord)
if err != nil {
return fmt.Errorf("找不到该批次的站内信记录: %w", err)
}
senderID := firstRecord.SenderID
senderType := firstRecord.SenderType
// 2. 根据新的目标接收群体获取接收人列表
var receiverIDs []uint64
var receiverType string
switch targetType {
case "platform":
receiverType = "platform"
var list []models.AdminUser
_, err := models.Orm.QueryTable(new(models.AdminUser)).Filter("status", 1).Filter("delete_time__isnull", true).All(&list, "id")
if err != nil {
return fmt.Errorf("查询平台用户失败: %w", err)
}
for _, u := range list {
receiverIDs = append(receiverIDs, u.ID)
}
case "tenant_all":
receiverType = "tenant"
var list []models.SystemTenantUser
_, err := models.Orm.QueryTable(new(models.SystemTenantUser)).Filter("status", 1).Filter("delete_time__isnull", true).All(&list, "id")
if err != nil {
return fmt.Errorf("查询租户用户失败: %w", err)
}
for _, u := range list {
receiverIDs = append(receiverIDs, u.ID)
}
case "role":
receiverType = "platform"
var list []models.AdminUser
_, err := models.Orm.QueryTable(new(models.AdminUser)).Filter("status", 1).Filter("role_id", targetRoleID).Filter("delete_time__isnull", true).All(&list, "id")
if err != nil {
return fmt.Errorf("根据角色查询用户失败: %w", err)
}
for _, u := range list {
receiverIDs = append(receiverIDs, u.ID)
}
case "tenant":
receiverType = "tenant"
var list []models.SystemTenantUser
_, err := models.Orm.QueryTable(new(models.SystemTenantUser)).Filter("status", 1).Filter("tid", targetTenantID).Filter("delete_time__isnull", true).All(&list, "id")
if err != nil {
return fmt.Errorf("根据租户查询用户失败: %w", err)
}
for _, u := range list {
receiverIDs = append(receiverIDs, u.ID)
}
default:
return fmt.Errorf("未知的发送目标类型: %s", targetType)
}
// 3. 获取该批次中现有的所有记录 (包括已删除的)
var existingRecords []models.SystemReminderList
_, err = models.Orm.QueryTable(new(models.SystemReminderList)).Filter("batch_id", batchID).All(&existingRecords)
if err != nil {
return fmt.Errorf("获取现有记录失败: %w", err)
}
// 建立 map 快速查找Key 为 "receiverType_receiverID"
existingMap := make(map[string]*models.SystemReminderList)
for i := range existingRecords {
key := fmt.Sprintf("%s_%d", existingRecords[i].ReceiverType, existingRecords[i].ReceiverID)
existingMap[key] = &existingRecords[i]
}
newReceiverMap := make(map[string]bool)
for _, rid := range receiverIDs {
key := fmt.Sprintf("%s_%d", receiverType, rid)
newReceiverMap[key] = true
}
now := time.Now()
// 事务处理
err = models.Orm.DoTx(func(c context.Context, txOrm orm.TxOrmer) error {
// A. 对于已经不在新接收者列表中的用户,软删除
for key, rec := range existingMap {
if !newReceiverMap[key] {
if rec.DeleteTime == nil {
rec.DeleteTime = &now
if _, e := txOrm.Update(rec, "DeleteTime"); e != nil {
return e
}
}
}
}
// B. 对于仍然在新接收者列表中的用户,更新标题、内容、以及 target 信息;如果原来被删除了,清除 delete_time
var newInserts []models.SystemReminderList
for _, rid := range receiverIDs {
key := fmt.Sprintf("%s_%d", receiverType, rid)
if rec, exists := existingMap[key]; exists {
rec.Title = title
rec.Content = content
rec.TargetType = targetType
rec.TargetRoleID = targetRoleID
rec.TargetTenantID = targetTenantID
cols := []string{"Title", "Content", "TargetType", "TargetRoleID", "TargetTenantID"}
if rec.DeleteTime != nil {
rec.DeleteTime = nil
rec.IsRead = 0
rec.ReadTime = nil
cols = append(cols, "DeleteTime", "IsRead", "ReadTime")
}
if _, e := txOrm.Update(rec, cols...); e != nil {
return e
}
} else {
// C. 对于新增加的接收者,插入新记录
newInserts = append(newInserts, models.SystemReminderList{
Title: title,
Content: content,
SenderID: senderID,
SenderType: senderType,
ReceiverID: rid,
ReceiverType: receiverType,
IsRead: 0,
CreateTime: &now,
BatchID: batchID,
TargetType: targetType,
TargetRoleID: targetRoleID,
TargetTenantID: targetTenantID,
})
}
}
if len(newInserts) > 0 {
if _, e := txOrm.InsertMulti(100, newInserts); e != nil {
return e
}
}
return nil
})
return err
}
// DeleteSentReminderBatch 删除已发送消息(删除该批次下所有记录)
func DeleteSentReminderBatch(batchID string) error {
now := time.Now()
_, err := models.Orm.QueryTable(new(models.SystemReminderList)).
Filter("batch_id", batchID).
Update(map[string]interface{}{
"delete_time": &now,
})
return err
}