package services import ( "context" "fmt" "strconv" "time" "github.com/beego/beego/v2/client/orm" "server/models" ) // GetSiteReminderConfig 获取站内信配置(从 yz_platform_normal_setting 读取) func GetSiteReminderConfig() (models.SystemSiteReminder, error) { retentionDaysStr := models.GetPlatformSettingValue("sitemsg_retention_days", "30") autoReadStr := models.GetPlatformSettingValue("sitemsg_auto_read", "0") retentionDays, _ := strconv.Atoi(retentionDaysStr) if retentionDays <= 0 { retentionDays = 30 } autoRead := int8(0) if autoReadStr == "1" { autoRead = 1 } now := time.Now() row := models.SystemSiteReminder{ ID: 1, RetentionDays: retentionDays, AutoRead: autoRead, CreateTime: &now, UpdateTime: &now, } return row, nil } // SaveSiteReminderConfig 保存/更新配置 func SaveSiteReminderConfig(retentionDays int, autoRead int8) error { if retentionDays <= 0 { retentionDays = 30 } autoReadStr := "0" if autoRead == 1 { autoReadStr = "1" } settings := []struct { code string name string value string remark string }{ {"sitemsg_retention_days", "站内信消息保留天数", strconv.Itoa(retentionDays), ""}, {"sitemsg_auto_read", "自动标记已读状态", autoReadStr, "0为关闭,1为开启"}, } for _, item := range settings { var setting models.PlatformNormalSetting err := models.Orm.QueryTable(new(models.PlatformNormalSetting)). Filter("code", item.code). Filter("delete_time__isnull", true). One(&setting) if err == nil { setting.Value = item.value setting.Name = item.name setting.Remark = item.remark now := time.Now() setting.UpdateTime = &now _, err = models.Orm.Update(&setting, "Value", "Name", "Remark", "UpdateTime") if err != nil { return err } } else { newSetting := models.PlatformNormalSetting{ Name: item.name, Code: item.code, Value: item.value, Remark: item.remark, CreateTime: time.Now(), } _, err = models.Orm.Insert(&newSetting) if err != nil { return err } } } return nil } // 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 }