diff --git a/src/Oms.Application/Dtos/Customers/CustomerPaginatedOutput.cs b/src/Oms.Application/Dtos/Customers/CustomerPaginatedOutput.cs
index 6ddc01a..cdcceb3 100644
--- a/src/Oms.Application/Dtos/Customers/CustomerPaginatedOutput.cs
+++ b/src/Oms.Application/Dtos/Customers/CustomerPaginatedOutput.cs
@@ -7,6 +7,11 @@ namespace Oms.Application.Dtos.Customers;
///
public class CustomerPaginatedOutput
{
+ ///
+ /// 主键。
+ ///
+ public int Id { get; set; }
+
///
/// 父客户ID,空表示主客户。
///
diff --git a/src/Oms.Application/Dtos/System/NoticeOutput.cs b/src/Oms.Application/Dtos/System/NoticeOutput.cs
index 39a4350..058f368 100644
--- a/src/Oms.Application/Dtos/System/NoticeOutput.cs
+++ b/src/Oms.Application/Dtos/System/NoticeOutput.cs
@@ -46,10 +46,15 @@ public class NoticeOutput
public string ReceiverTypeName { get; set; } = string.Empty;
///
- /// 接收主体 ID 列表。
+ /// 接收主体 ID 列表(持久化为 user.userid)。
///
public List ReceiverIds { get; set; } = [];
+ ///
+ /// 表单回显用选中 ID:user 与 一致;customer 为 t_customer.Id。
+ ///
+ public List ReceiverSelectionIds { get; set; } = [];
+
///
/// 已读人数。
///
diff --git a/src/Oms.Application/Dtos/System/NoticeReceiverOptionOutput.cs b/src/Oms.Application/Dtos/System/NoticeReceiverOptionOutput.cs
new file mode 100644
index 0000000..103556f
--- /dev/null
+++ b/src/Oms.Application/Dtos/System/NoticeReceiverOptionOutput.cs
@@ -0,0 +1,22 @@
+namespace Oms.Application.Dtos.System;
+
+///
+/// 公告接收人候选项。
+///
+public class NoticeReceiverOptionOutput
+{
+ ///
+ /// 表单选中值:user 为 user.userid,customer 为 t_customer.Id。
+ ///
+ public int Value { get; set; }
+
+ ///
+ /// 展示名称。
+ ///
+ public string Label { get; set; } = string.Empty;
+
+ ///
+ /// 辅助说明(账号、客户编码等)。
+ ///
+ public string? Extra { get; set; }
+}
diff --git a/src/Oms.Application/Dtos/System/NoticeReceiverOptionsInput.cs b/src/Oms.Application/Dtos/System/NoticeReceiverOptionsInput.cs
new file mode 100644
index 0000000..be0f1ea
--- /dev/null
+++ b/src/Oms.Application/Dtos/System/NoticeReceiverOptionsInput.cs
@@ -0,0 +1,27 @@
+namespace Oms.Application.Dtos.System;
+
+///
+/// 公告接收人候选项查询入参。
+///
+public class NoticeReceiverOptionsInput
+{
+ ///
+ /// 接收对象类型:user/customer。
+ ///
+ public string ReceiverType { get; set; } = string.Empty;
+
+ ///
+ /// 关键字。
+ ///
+ public string? Keyword { get; set; }
+
+ ///
+ /// 页码。
+ ///
+ public int Page { get; set; } = 1;
+
+ ///
+ /// 每页条数。
+ ///
+ public int PageSize { get; set; } = 50;
+}
diff --git a/src/Oms.Application/Services/System/NoticeService.cs b/src/Oms.Application/Services/System/NoticeService.cs
index faa8933..9393413 100644
--- a/src/Oms.Application/Services/System/NoticeService.cs
+++ b/src/Oms.Application/Services/System/NoticeService.cs
@@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore;
using Oms.Application.Dtos.Shared;
using Oms.Application.Dtos.System;
using Oms.Application.Shared;
+using Oms.Domain.CustomersAggregate.Entities;
using Oms.Domain.CustomersAggregate.Repositories;
using Oms.Domain.SystemAggregate.Entities;
using Oms.Domain.SystemAggregate.Repositories;
@@ -21,6 +22,21 @@ public class NoticeService : ApplicationService
private const string NoticeTypeNotice = "notice";
private const string NoticeTypeMessage = "message";
private const string ReceiverTypeUser = "user";
+ private const string ReceiverTypeCustomer = "customer";
+
+ private const string InternalUserFilter = """
+ (
+ NOT EXISTS (SELECT 1 FROM `user_role` ur0 WHERE ur0.`UserId` = u.`Id`)
+ OR EXISTS (
+ SELECT 1
+ FROM `user_role` ur
+ INNER JOIN `role` r ON r.`Id` = ur.`RoleId` AND r.`IsDeleted` = 0
+ WHERE ur.`UserId` = u.`Id`
+ AND r.`Code` <> 'Customer'
+ AND r.`Code` NOT LIKE 'CustomerSub\_%'
+ )
+ )
+ """;
private INoticeRepository NoticeRepository => LazyServiceProvider.GetRequiredService();
private INoticeReadRepository NoticeReadRepository => LazyServiceProvider.GetRequiredService();
@@ -30,8 +46,6 @@ public class NoticeService : ApplicationService
///
/// 查询通知列表。
///
- /// 入参:通知分页查询条件。
- /// 通知分页结果,包含通知主体、接收范围与已读统计。
[HttpGet("/system/notice/list")]
public async Task> GetListAsync([FromQuery] NoticeListInput input)
{
@@ -55,20 +69,40 @@ public class NoticeService : ApplicationService
x => x.Key,
x => (int)x.Select(item => $"{item.ReceiverType}:{item.ReceiverId}").Distinct(StringComparer.Ordinal).Count());
+ var selectionIdMap = await BuildReceiverSelectionIdMapAsync(notices);
+
return new OmsPageResult
{
Items = notices.Select(x => MapNoticeOutput(
x,
- readCountMap.TryGetValue(x.Id, out var readCount) ? readCount : 0)).ToList(),
+ readCountMap.TryGetValue(x.Id, out var readCount) ? readCount : 0,
+ selectionIdMap.TryGetValue(x.Id, out var selectionIds) ? selectionIds : [])).ToList(),
Total = total
};
}
+ ///
+ /// 接收人候选项(按接收对象类型加载)。
+ ///
+ [HttpGet("/system/notice/receiver-options")]
+ public async Task> GetReceiverOptionsAsync([FromQuery] NoticeReceiverOptionsInput input)
+ {
+ var receiverType = NormalizeReceiverType(input.ReceiverType);
+ var pageIndex = OmsDocHelper.NormalizePage(input.Page);
+ var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
+ var keyword = input.Keyword?.Trim();
+
+ if (receiverType == ReceiverTypeCustomer)
+ {
+ return await QueryCustomerReceiverOptionsAsync(keyword, pageIndex, pageSize);
+ }
+
+ return await QueryUserReceiverOptionsAsync(keyword, pageIndex, pageSize);
+ }
+
///
/// 新增通知。
///
- /// 入参:通知新增参数。
- /// 无返回体,仅完成新增通知。
[HttpPost("/system/notice/create")]
public async Task CreateAsync(NoticeCreateInput input)
{
@@ -88,8 +122,6 @@ public class NoticeService : ApplicationService
///
/// 更新通知。
///
- /// 入参:通知更新参数。
- /// 无返回体,仅完成更新通知。
[HttpPost("/system/notice/update")]
public async Task UpdateAsync(NoticeUpdateInput input)
{
@@ -106,7 +138,7 @@ public class NoticeService : ApplicationService
var existsReads = await NoticeReadRepository.GetListAsync(x => x.NoticeId == entity.Id);
var receiverIdSet = receiverIds.ToHashSet();
- foreach (var read in existsReads.Where(x => x.ReceiverType != entity.ReceiverType || !receiverIdSet.Contains(x.ReceiverId)))
+ foreach (var read in existsReads.Where(x => x.ReceiverType != ReceiverTypeUser || !receiverIdSet.Contains(x.ReceiverId)))
{
await NoticeReadRepository.DeleteAsync(read);
}
@@ -115,8 +147,6 @@ public class NoticeService : ApplicationService
///
/// 删除通知。
///
- /// 入参:目标主键。
- /// 无返回体,仅完成删除通知。
[HttpPost("/system/notice/delete")]
public async Task DeleteAsync(IdInput input)
{
@@ -127,8 +157,6 @@ public class NoticeService : ApplicationService
///
/// 确认通知已读。
///
- /// 入参:目标通知主键。
- /// 无返回体,仅完成当前主体的已读确认。
[HttpPost("/system/notice/read-confirm")]
public async Task ReadConfirmAsync(IdInput input)
{
@@ -141,8 +169,7 @@ public class NoticeService : ApplicationService
throw new BusinessException("当前登录主体无效");
}
- if (!string.Equals(notice.ReceiverType, currentReceiverType, StringComparison.OrdinalIgnoreCase) ||
- !receiverIds.Contains(currentReceiverId))
+ if (!receiverIds.Contains(currentReceiverId))
{
throw new BusinessException("当前主体不在通知接收范围内");
}
@@ -162,6 +189,131 @@ public class NoticeService : ApplicationService
});
}
+ private async Task> QueryUserReceiverOptionsAsync(
+ string? keyword,
+ int pageIndex,
+ int pageSize)
+ {
+ var where = $"WHERE u.`IsDeleted` = 0 AND {InternalUserFilter}";
+ var args = new List