1.06 #10

Merged
zzh merged 18 commits from test into master 2026-10-19 17:03:15 +08:00
4 changed files with 58 additions and 42 deletions
Showing only changes of commit 7a0affe2d4 - Show all commits

2
db.sql
View File

@ -1220,7 +1220,7 @@ CREATE TABLE `t_notice` (
`Content` TEXT NOT NULL COMMENT '通知内容', `Content` TEXT NOT NULL COMMENT '通知内容',
`IsPopup` TINYINT NOT NULL DEFAULT 0 COMMENT '是否弹窗:0-否,1-是', `IsPopup` TINYINT NOT NULL DEFAULT 0 COMMENT '是否弹窗:0-否,1-是',
`ReceiverType` VARCHAR(16) NOT NULL COMMENT '接收对象:user/customer', `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-是', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳', `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人', `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',

View File

@ -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;

View File

@ -65,22 +65,25 @@ public class NoticeService : ApplicationService
.Select(x => x.NoticeId) .Select(x => x.NoticeId)
.Distinct() .Distinct()
.ToList(); .ToList();
var myReadIdSet = myReadNoticeIds.ToHashSet();
var predicate = manageAll // 先按类型/关键词查,再在内存按 ReceiverIds 判可见性。
? BuildManageListPredicate(input) // MySQL JSON 列与字符串 "[]" 比较会失败,导致广播对客户/收件箱不可见。
: BuildListPredicate( var basePredicate = BuildManageListPredicate(input);
input, var candidates = await NoticeRepository.GetListAsync(basePredicate);
receiverUserId, var visible = candidates
expectedReceiverType, .Where(x => manageAll || IsVisibleToReceiver(x, receiverUserId, expectedReceiverType))
input.OnlyUnread == true ? myReadNoticeIds : null); .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 pageIndex = OmsDocHelper.NormalizePage(input.Page);
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize); var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
var notices = await NoticeRepository.GetPagedListAsync( var total = visible.Count;
predicate, var notices = visible
pageIndex, .Skip(Math.Max(pageIndex - 1, 0) * pageSize)
pageSize, .Take(pageSize)
new Dictionary<string, bool> { [nameof(Notice.CreateTime)] = false, [nameof(Notice.Id)] = false }); .ToList();
var total = await NoticeRepository.CountAsync(predicate);
var noticeIds = notices.Select(x => x.Id).Distinct().ToArray(); var noticeIds = notices.Select(x => x.Id).Distinct().ToArray();
var reads = noticeIds.Length == 0 var reads = noticeIds.Length == 0
@ -91,8 +94,6 @@ public class NoticeService : ApplicationService
.ToDictionary( .ToDictionary(
x => x.Key, x => x.Key,
x => (int)x.Select(item => $"{item.ReceiverType}:{item.ReceiverId}").Distinct(StringComparer.Ordinal).Count()); x => (int)x.Select(item => $"{item.ReceiverType}:{item.ReceiverId}").Distinct(StringComparer.Ordinal).Count());
var myReadIdSet = myReadNoticeIds.ToHashSet();
var selectionIdMap = await BuildReceiverSelectionIdMapAsync(notices); var selectionIdMap = await BuildReceiverSelectionIdMapAsync(notices);
return new OmsPageResult<NoticeOutput> return new OmsPageResult<NoticeOutput>
@ -365,33 +366,19 @@ public class NoticeService : ApplicationService
x.Content.Contains(keyword)); x.Content.Contains(keyword));
} }
private static Expression<Func<Notice, bool>> BuildListPredicate( /// <summary>
NoticeListInput input, /// 空接收人 = 全体广播(员工/客户均可见);否则须类型匹配且 mapId 在名单内。
int receiverUserId, /// </summary>
string expectedReceiverType, private static bool IsVisibleToReceiver(Notice notice, int receiverUserId, string expectedReceiverType)
IReadOnlyCollection<int>? excludeNoticeIds)
{ {
var noticeType = input.NoticeType?.Trim(); var receiverIds = OmsDocHelper.DeserializeJson<List<int>>(notice.ReceiverIds) ?? [];
var receiverType = input.ReceiverType?.Trim(); if (receiverIds.Count == 0)
var keyword = input.Keyword?.Trim(); {
var receiverIdToken = receiverUserId.ToString(); return true;
var excluded = excludeNoticeIds?.Where(x => x > 0).Distinct().ToList() ?? []; }
return x =>
(string.IsNullOrWhiteSpace(noticeType) || x.NoticeType == noticeType) && return string.Equals(notice.ReceiverType, expectedReceiverType, StringComparison.OrdinalIgnoreCase)
(string.IsNullOrWhiteSpace(receiverType) || x.ReceiverType == receiverType) && && receiverIds.Contains(receiverUserId);
(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));
} }
private async Task<Notice> GetNoticeOrThrowAsync(int id) private async Task<Notice> GetNoticeOrThrowAsync(int id)

View File

@ -16,5 +16,9 @@ public class Notice : OmsAuditedAggregateRoot
public string ReceiverType { get; set; } = string.Empty; public string ReceiverType { get; set; } = string.Empty;
/// <summary>
/// 接收人 mapId 列表 JSON 数组字符串,如 [] / [1,2]。用 text 存储,避免 MySQL JSON 列与字符串比较失败导致广播不可见。
/// </summary>
[SugarColumn(ColumnDataType = "text")]
public string ReceiverIds { get; set; } = "[]"; public string ReceiverIds { get; set; } = "[]";
} }