From 7a0affe2d48e3eb70e0cb70c077b146cd8ac994d Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Fri, 16 Oct 2026 09:57:25 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=85=AC=E5=91=8A=E5=B9=BF=E6=92=AD?= =?UTF-8?q?=E5=8F=AF=E8=A7=81=E6=80=A7=E6=94=B9=E4=B8=BA=E5=8F=8D=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E5=88=A4=E5=AE=9A=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=20JSON=20=E5=88=97=E6=AF=94=E8=BE=83=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ReceiverIds 改为 text 存储并补充迁移;广播对客户/收件箱可见。 Co-authored-by: Cursor --- db.sql | 2 +- sql/migrate_notice_receiver_ids_text.sql | 25 +++++++ .../Services/System/NoticeService.cs | 69 ++++++++----------- .../SystemAggregate/Entities/Notice.cs | 4 ++ 4 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 sql/migrate_notice_receiver_ids_text.sql diff --git a/db.sql b/db.sql index 3f1111e..2661036 100644 --- a/db.sql +++ b/db.sql @@ -1220,7 +1220,7 @@ CREATE TABLE `t_notice` ( `Content` TEXT NOT NULL COMMENT '通知内容', `IsPopup` TINYINT NOT NULL DEFAULT 0 COMMENT '是否弹窗:0-否,1-是', `ReceiverType` VARCHAR(16) NOT NULL COMMENT '接收对象:user/customer', - `ReceiverIds` JSON NOT NULL COMMENT '接收对象ID列表(JSON数组)', + `ReceiverIds` TEXT NOT NULL COMMENT '接收对象ID列表(JSON数组字符串)', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳', `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人', diff --git a/sql/migrate_notice_receiver_ids_text.sql b/sql/migrate_notice_receiver_ids_text.sql new file mode 100644 index 0000000..d4bcd97 --- /dev/null +++ b/sql/migrate_notice_receiver_ids_text.sql @@ -0,0 +1,25 @@ +-- ============================================================================= +-- migrate_notice_receiver_ids_text.sql +-- t_notice.ReceiverIds:JSON → TEXT,保证广播([])与定向名单可用字符串条件查询 +-- 可重复执行 +-- ============================================================================= +SET @db := DATABASE(); + +SET @col_type := ( + SELECT DATA_TYPE + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db + AND TABLE_NAME = 't_notice' + AND COLUMN_NAME = 'ReceiverIds' + LIMIT 1 +); + +SET @sql := IF( + @col_type IS NOT NULL AND LOWER(IFNULL(@col_type, '')) = 'json', + 'ALTER TABLE `t_notice` + MODIFY COLUMN `ReceiverIds` TEXT NOT NULL COMMENT ''接收对象ID列表(JSON数组字符串)''', + 'SELECT ''t_notice.ReceiverIds already text or missing'' AS info' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; diff --git a/src/Oms.Application/Services/System/NoticeService.cs b/src/Oms.Application/Services/System/NoticeService.cs index f01b26d..e3238b6 100644 --- a/src/Oms.Application/Services/System/NoticeService.cs +++ b/src/Oms.Application/Services/System/NoticeService.cs @@ -65,22 +65,25 @@ public class NoticeService : ApplicationService .Select(x => x.NoticeId) .Distinct() .ToList(); + var myReadIdSet = myReadNoticeIds.ToHashSet(); - var predicate = manageAll - ? BuildManageListPredicate(input) - : BuildListPredicate( - input, - receiverUserId, - expectedReceiverType, - input.OnlyUnread == true ? myReadNoticeIds : null); + // 先按类型/关键词查,再在内存按 ReceiverIds 判可见性。 + // MySQL JSON 列与字符串 "[]" 比较会失败,导致广播对客户/收件箱不可见。 + var basePredicate = BuildManageListPredicate(input); + var candidates = await NoticeRepository.GetListAsync(basePredicate); + var visible = candidates + .Where(x => manageAll || IsVisibleToReceiver(x, receiverUserId, expectedReceiverType)) + .Where(x => input.OnlyUnread != true || !myReadIdSet.Contains(x.Id)) + .OrderByDescending(x => x.CreateTime) + .ThenByDescending(x => x.Id) + .ToList(); var pageIndex = OmsDocHelper.NormalizePage(input.Page); var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize); - var notices = await NoticeRepository.GetPagedListAsync( - predicate, - pageIndex, - pageSize, - new Dictionary { [nameof(Notice.CreateTime)] = false, [nameof(Notice.Id)] = false }); - var total = await NoticeRepository.CountAsync(predicate); + var total = visible.Count; + var notices = visible + .Skip(Math.Max(pageIndex - 1, 0) * pageSize) + .Take(pageSize) + .ToList(); var noticeIds = notices.Select(x => x.Id).Distinct().ToArray(); var reads = noticeIds.Length == 0 @@ -91,8 +94,6 @@ public class NoticeService : ApplicationService .ToDictionary( x => x.Key, x => (int)x.Select(item => $"{item.ReceiverType}:{item.ReceiverId}").Distinct(StringComparer.Ordinal).Count()); - var myReadIdSet = myReadNoticeIds.ToHashSet(); - var selectionIdMap = await BuildReceiverSelectionIdMapAsync(notices); return new OmsPageResult @@ -365,33 +366,19 @@ public class NoticeService : ApplicationService x.Content.Contains(keyword)); } - private static Expression> BuildListPredicate( - NoticeListInput input, - int receiverUserId, - string expectedReceiverType, - IReadOnlyCollection? excludeNoticeIds) + /// + /// 空接收人 = 全体广播(员工/客户均可见);否则须类型匹配且 mapId 在名单内。 + /// + private static bool IsVisibleToReceiver(Notice notice, int receiverUserId, string expectedReceiverType) { - var noticeType = input.NoticeType?.Trim(); - var receiverType = input.ReceiverType?.Trim(); - var keyword = input.Keyword?.Trim(); - var receiverIdToken = receiverUserId.ToString(); - var excluded = excludeNoticeIds?.Where(x => x > 0).Distinct().ToList() ?? []; - return x => - (string.IsNullOrWhiteSpace(noticeType) || x.NoticeType == noticeType) && - (string.IsNullOrWhiteSpace(receiverType) || x.ReceiverType == receiverType) && - (string.IsNullOrWhiteSpace(keyword) || - x.Title.Contains(keyword) || - x.Content.Contains(keyword)) && - ( - // 空接收人列表 = 全体广播(员工/客户均可见) - x.ReceiverIds == "[]" || - (x.ReceiverType == expectedReceiverType && - (x.ReceiverIds == $"[{receiverIdToken}]" || - x.ReceiverIds.StartsWith($"[{receiverIdToken},") || - x.ReceiverIds.Contains($",{receiverIdToken},") || - x.ReceiverIds.EndsWith($",{receiverIdToken}]"))) - ) && - (excluded.Count == 0 || !excluded.Contains(x.Id)); + var receiverIds = OmsDocHelper.DeserializeJson>(notice.ReceiverIds) ?? []; + if (receiverIds.Count == 0) + { + return true; + } + + return string.Equals(notice.ReceiverType, expectedReceiverType, StringComparison.OrdinalIgnoreCase) + && receiverIds.Contains(receiverUserId); } private async Task GetNoticeOrThrowAsync(int id) diff --git a/src/Oms.Domain/SystemAggregate/Entities/Notice.cs b/src/Oms.Domain/SystemAggregate/Entities/Notice.cs index e8fb134..3b2afa6 100644 --- a/src/Oms.Domain/SystemAggregate/Entities/Notice.cs +++ b/src/Oms.Domain/SystemAggregate/Entities/Notice.cs @@ -16,5 +16,9 @@ public class Notice : OmsAuditedAggregateRoot public string ReceiverType { get; set; } = string.Empty; + /// + /// 接收人 mapId 列表 JSON 数组字符串,如 [] / [1,2]。用 text 存储,避免 MySQL JSON 列与字符串比较失败导致广播不可见。 + /// + [SugarColumn(ColumnDataType = "text")] public string ReceiverIds { get; set; } = "[]"; }