feat: 优化公告通知接收人过滤与客户删除同步

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-07-11 11:22:21 +08:00
parent 36525ca688
commit 8ac7e6487c
3 changed files with 85 additions and 15 deletions

View File

@ -15,11 +15,10 @@ public class NoticeCreateInput
public string NoticeType { get; set; } = string.Empty; public string NoticeType { get; set; } = string.Empty;
/// <summary> /// <summary>
/// 标题。 /// 标题(公告必填;站内信可空)。
/// </summary> /// </summary>
[Required]
[StringLength(128)] [StringLength(128)]
public string Title { get; set; } = string.Empty; public string? Title { get; set; }
/// <summary> /// <summary>
/// 正文内容。 /// 正文内容。

View File

@ -227,7 +227,54 @@ public class CustomerService : ApplicationService
public async Task DeleteAsync(IdInput input) public async Task DeleteAsync(IdInput input)
{ {
var entity = await GetCustomerOrThrowAsync(input.Id); var entity = await GetCustomerOrThrowAsync(input.Id);
if (!entity.ParentCustomerId.HasValue
&& await CustomerRepository.AnyAsync(x => x.ParentCustomerId == entity.Id))
{
throw new BusinessException("存在子客户,请先删除子客户");
}
await CustomerRepository.DeleteAsync(entity); await CustomerRepository.DeleteAsync(entity);
await DeleteLinkedRbacUserAsync(entity);
}
private async Task DeleteLinkedRbacUserAsync(Customer entity)
{
var userGuid = await GetRbacUserGuidByMapIdAsync(entity.UserId);
if (userGuid.HasValue && userGuid.Value != Guid.Empty)
{
await UserService.DeleteAsync(userGuid.Value);
}
if (entity.ParentCustomerId.HasValue)
{
await TryDeleteSubCustomerRoleAsync(entity.UserId);
}
}
private async Task<Guid?> GetRbacUserGuidByMapIdAsync(int mapId)
{
return await RbacDbContext.Database.SqlQuery<Guid?>(
$"select `Id` as `Value` from `user` where `userid` = {mapId} and `IsDeleted` = 0")
.FirstOrDefaultAsync();
}
private async Task TryDeleteSubCustomerRoleAsync(int userId)
{
try
{
var roles = await RoleService.GetAllRolesAsync();
var role = roles.FirstOrDefault(x =>
string.Equals(x.Code, $"CustomerSub_{userId}", StringComparison.OrdinalIgnoreCase));
if (role != null)
{
await RoleService.DeleteAsync(role.Id);
}
}
catch
{
// 子客户角色清理失败不阻断客户删除
}
} }
/// <summary> /// <summary>

View File

@ -49,7 +49,17 @@ public class NoticeService : ApplicationService
[HttpGet("/system/notice/list")] [HttpGet("/system/notice/list")]
public async Task<OmsPageResult<NoticeOutput>> GetListAsync([FromQuery] NoticeListInput input) public async Task<OmsPageResult<NoticeOutput>> GetListAsync([FromQuery] NoticeListInput input)
{ {
var predicate = BuildListPredicate(input); var receiverUserId = CurrentUser.GetId<int>();
if (receiverUserId <= 0)
{
return new OmsPageResult<NoticeOutput> { Items = [], Total = 0 };
}
var expectedReceiverType = OmsSessionHelper.IsCustomerLogin(CurrentUser)
? ReceiverTypeCustomer
: ReceiverTypeUser;
var predicate = BuildListPredicate(input, receiverUserId, expectedReceiverType);
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 notices = await NoticeRepository.GetPagedListAsync(
@ -106,12 +116,13 @@ public class NoticeService : ApplicationService
[HttpPost("/system/notice/create")] [HttpPost("/system/notice/create")]
public async Task CreateAsync(NoticeCreateInput input) public async Task CreateAsync(NoticeCreateInput input)
{ {
ValidateNoticeText(input.Title, input.Content); var noticeType = NormalizeNoticeType(input.NoticeType);
ValidateNoticeText(noticeType, input.Title, input.Content);
var receiverIds = await NormalizeAndValidateReceiverIdsAsync(input.ReceiverType, input.ReceiverIds); var receiverIds = await NormalizeAndValidateReceiverIdsAsync(input.ReceiverType, input.ReceiverIds);
await NoticeRepository.InsertAsync(new Notice await NoticeRepository.InsertAsync(new Notice
{ {
NoticeType = NormalizeNoticeType(input.NoticeType), NoticeType = noticeType,
Title = input.Title.Trim(), Title = NormalizeTitle(input.Title),
Content = input.Content.Trim(), Content = input.Content.Trim(),
IsPopup = NormalizePopupValue(input.IsPopup), IsPopup = NormalizePopupValue(input.IsPopup),
ReceiverType = NormalizeReceiverType(input.ReceiverType), ReceiverType = NormalizeReceiverType(input.ReceiverType),
@ -126,10 +137,11 @@ public class NoticeService : ApplicationService
public async Task UpdateAsync(NoticeUpdateInput input) public async Task UpdateAsync(NoticeUpdateInput input)
{ {
var entity = await GetNoticeOrThrowAsync(input.Id); var entity = await GetNoticeOrThrowAsync(input.Id);
ValidateNoticeText(input.Title, input.Content); var noticeType = NormalizeNoticeType(input.NoticeType);
ValidateNoticeText(noticeType, input.Title, input.Content);
var receiverIds = await NormalizeAndValidateReceiverIdsAsync(input.ReceiverType, input.ReceiverIds); var receiverIds = await NormalizeAndValidateReceiverIdsAsync(input.ReceiverType, input.ReceiverIds);
entity.NoticeType = NormalizeNoticeType(input.NoticeType); entity.NoticeType = noticeType;
entity.Title = input.Title.Trim(); entity.Title = NormalizeTitle(input.Title);
entity.Content = input.Content.Trim(); entity.Content = input.Content.Trim();
entity.IsPopup = NormalizePopupValue(input.IsPopup); entity.IsPopup = NormalizePopupValue(input.IsPopup);
entity.ReceiverType = NormalizeReceiverType(input.ReceiverType); entity.ReceiverType = NormalizeReceiverType(input.ReceiverType);
@ -162,7 +174,7 @@ public class NoticeService : ApplicationService
{ {
var notice = await GetNoticeOrThrowAsync(input.Id); var notice = await GetNoticeOrThrowAsync(input.Id);
var receiverIds = OmsDocHelper.DeserializeJson<List<int>>(notice.ReceiverIds) ?? []; var receiverIds = OmsDocHelper.DeserializeJson<List<int>>(notice.ReceiverIds) ?? [];
var currentReceiverType = ReceiverTypeUser; var currentReceiverType = NormalizeReceiverType(notice.ReceiverType);
var currentReceiverId = CurrentUser.GetId<int>(); var currentReceiverId = CurrentUser.GetId<int>();
if (currentReceiverId <= 0) if (currentReceiverId <= 0)
{ {
@ -314,17 +326,26 @@ public class NoticeService : ApplicationService
return result; return result;
} }
private static Expression<Func<Notice, bool>> BuildListPredicate(NoticeListInput input) private static Expression<Func<Notice, bool>> BuildListPredicate(
NoticeListInput input,
int receiverUserId,
string expectedReceiverType)
{ {
var noticeType = input.NoticeType?.Trim(); var noticeType = input.NoticeType?.Trim();
var receiverType = input.ReceiverType?.Trim(); var receiverType = input.ReceiverType?.Trim();
var keyword = input.Keyword?.Trim(); var keyword = input.Keyword?.Trim();
var receiverIdToken = receiverUserId.ToString();
return x => return x =>
(string.IsNullOrWhiteSpace(noticeType) || x.NoticeType == noticeType) && (string.IsNullOrWhiteSpace(noticeType) || x.NoticeType == noticeType) &&
(string.IsNullOrWhiteSpace(receiverType) || x.ReceiverType == receiverType) && (string.IsNullOrWhiteSpace(receiverType) || x.ReceiverType == receiverType) &&
(string.IsNullOrWhiteSpace(keyword) || (string.IsNullOrWhiteSpace(keyword) ||
x.Title.Contains(keyword) || x.Title.Contains(keyword) ||
x.Content.Contains(keyword)); x.Content.Contains(keyword)) &&
x.ReceiverType == expectedReceiverType &&
(x.ReceiverIds == $"[{receiverIdToken}]" ||
x.ReceiverIds.StartsWith($"[{receiverIdToken},") ||
x.ReceiverIds.Contains($",{receiverIdToken},") ||
x.ReceiverIds.EndsWith($",{receiverIdToken}]"));
} }
private async Task<Notice> GetNoticeOrThrowAsync(int id) private async Task<Notice> GetNoticeOrThrowAsync(int id)
@ -397,9 +418,9 @@ public class NoticeService : ApplicationService
.ToListAsync(); .ToListAsync();
} }
private static void ValidateNoticeText(string title, string content) private static void ValidateNoticeText(string noticeType, string? title, string content)
{ {
if (string.IsNullOrWhiteSpace(title)) if (noticeType == NoticeTypeNotice && string.IsNullOrWhiteSpace(title))
{ {
throw new BusinessException("通知标题不能为空"); throw new BusinessException("通知标题不能为空");
} }
@ -410,6 +431,9 @@ public class NoticeService : ApplicationService
} }
} }
private static string NormalizeTitle(string? title) =>
string.IsNullOrWhiteSpace(title) ? string.Empty : title.Trim();
private static NoticeOutput MapNoticeOutput(Notice entity, int readCount, List<int> receiverSelectionIds) private static NoticeOutput MapNoticeOutput(Notice entity, int readCount, List<int> receiverSelectionIds)
{ {
var receiverIds = OmsDocHelper.DeserializeJson<List<int>>(entity.ReceiverIds) ?? []; var receiverIds = OmsDocHelper.DeserializeJson<List<int>>(entity.ReceiverIds) ?? [];