From d49573fe9a5d4b17dcfad3911b3e67f46e6aa2af Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Mon, 6 Jul 2026 14:58:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(channel):=20=E6=96=B0=E5=A2=9E=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E6=A8=A1=E6=9D=BF=20CRUD=20=E4=B8=8E=20options=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持按组织与问题类型维护通知模板,含建表脚本与仓储层唯一性校验。 --- db.sql | 19 ++ sql/migrate_t_issue_template.sql | 18 ++ .../Dtos/Channels/IssueTemplateListInput.cs | 19 ++ .../Channels/IssueTemplateOptionOutput.cs | 15 + .../Dtos/Channels/IssueTemplateOutput.cs | 31 ++ .../Dtos/Channels/IssueTemplateSaveInput.cs | 23 ++ .../Services/Channels/IssueTemplateService.cs | 303 ++++++++++++++++++ .../Consts/IssueNotifyMethodCodes.cs | 57 ++++ .../Consts/IssueTypeCodes.cs | 38 +++ .../Entities/IssueTemplate.cs | 21 ++ .../Repositories/IIssueTemplateRepository.cs | 13 + .../Channels/IssueTemplateRepository.cs | 27 ++ 12 files changed, 584 insertions(+) create mode 100644 sql/migrate_t_issue_template.sql create mode 100644 src/Oms.Application/Dtos/Channels/IssueTemplateListInput.cs create mode 100644 src/Oms.Application/Dtos/Channels/IssueTemplateOptionOutput.cs create mode 100644 src/Oms.Application/Dtos/Channels/IssueTemplateOutput.cs create mode 100644 src/Oms.Application/Dtos/Channels/IssueTemplateSaveInput.cs create mode 100644 src/Oms.Application/Services/Channels/IssueTemplateService.cs create mode 100644 src/Oms.Domain/ChannelsAggregate/Consts/IssueNotifyMethodCodes.cs create mode 100644 src/Oms.Domain/ChannelsAggregate/Consts/IssueTypeCodes.cs create mode 100644 src/Oms.Domain/ChannelsAggregate/Entities/IssueTemplate.cs create mode 100644 src/Oms.Domain/ChannelsAggregate/Repositories/IIssueTemplateRepository.cs create mode 100644 src/Oms.SqlSugarCore/Channels/IssueTemplateRepository.cs diff --git a/db.sql b/db.sql index 9f81c85..c37356e 100644 --- a/db.sql +++ b/db.sql @@ -25,6 +25,7 @@ DROP TABLE IF EXISTS `t_label_print_record`; DROP TABLE IF EXISTS `t_outbound_record`; DROP TABLE IF EXISTS `t_inbound_record`; DROP TABLE IF EXISTS `t_label_template`; +DROP TABLE IF EXISTS `t_issue_template`; DROP TABLE IF EXISTS `t_auto_trigger`; DROP TABLE IF EXISTS `t_weight_rounding_info`; DROP TABLE IF EXISTS `t_weight_rounding`; @@ -653,6 +654,24 @@ CREATE TABLE `t_auto_trigger` ( KEY `idx_at_operation` (`Operation`) ) COMMENT='自动触发表'; +CREATE TABLE `t_issue_template` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + `OrganizationId` CHAR(36) NULL COMMENT '归属组织 GUID;NULL=平台通用', + `IssueType` VARCHAR(32) NOT NULL COMMENT '问题类型 code', + `Title` VARCHAR(128) NOT NULL COMMENT '问题标题', + `Content` TEXT NOT NULL COMMENT '问题内容', + `NotifyMethod` VARCHAR(64) NOT NULL COMMENT '通知方式,逗号分隔', + `Status` TINYINT NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用', + `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', + `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人', + `CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `UpdateUserId` INT NULL COMMENT '更新人', + `UpdateTime` DATETIME NULL COMMENT '更新时间', + PRIMARY KEY (`Id`), + KEY `idx_issue_tpl_org_type` (`OrganizationId`, `IssueType`, `IsDeleted`), + KEY `idx_issue_tpl_status` (`Status`, `IsDeleted`) +) COMMENT='渠道-问题模板'; + CREATE TABLE `t_label_template` ( `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1', `Name` VARCHAR(128) NOT NULL COMMENT '模板名称', diff --git a/sql/migrate_t_issue_template.sql b/sql/migrate_t_issue_template.sql new file mode 100644 index 0000000..f88142d --- /dev/null +++ b/sql/migrate_t_issue_template.sql @@ -0,0 +1,18 @@ +-- 渠道-问题模板 +CREATE TABLE IF NOT EXISTS `t_issue_template` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `OrganizationId` CHAR(36) NULL COMMENT '归属组织 GUID;NULL=平台通用', + `IssueType` VARCHAR(32) NOT NULL COMMENT '问题类型 code', + `Title` VARCHAR(128) NOT NULL COMMENT '问题标题', + `Content` TEXT NOT NULL COMMENT '问题内容', + `NotifyMethod` VARCHAR(64) NOT NULL COMMENT '通知方式,逗号分隔', + `Status` TINYINT NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用', + `IsDeleted` TINYINT NOT NULL DEFAULT 0, + `CreateUserId` INT NOT NULL, + `CreateTime` DATETIME NOT NULL, + `UpdateUserId` INT NULL, + `UpdateTime` DATETIME NULL, + PRIMARY KEY (`Id`), + KEY `idx_issue_tpl_org_type` (`OrganizationId`, `IssueType`, `IsDeleted`), + KEY `idx_issue_tpl_status` (`Status`, `IsDeleted`) +) COMMENT='渠道-问题模板'; diff --git a/src/Oms.Application/Dtos/Channels/IssueTemplateListInput.cs b/src/Oms.Application/Dtos/Channels/IssueTemplateListInput.cs new file mode 100644 index 0000000..3276368 --- /dev/null +++ b/src/Oms.Application/Dtos/Channels/IssueTemplateListInput.cs @@ -0,0 +1,19 @@ +namespace Oms.Application.Dtos.Channels; + +/// +/// 问题模板列表查询入参。 +/// +public class IssueTemplateListInput +{ + public int Page { get; set; } = 1; + + public int PageSize { get; set; } = 20; + + public string? OrganizationId { get; set; } + + public string? IssueType { get; set; } + + public string? Keyword { get; set; } + + public byte? Status { get; set; } +} diff --git a/src/Oms.Application/Dtos/Channels/IssueTemplateOptionOutput.cs b/src/Oms.Application/Dtos/Channels/IssueTemplateOptionOutput.cs new file mode 100644 index 0000000..32030cf --- /dev/null +++ b/src/Oms.Application/Dtos/Channels/IssueTemplateOptionOutput.cs @@ -0,0 +1,15 @@ +namespace Oms.Application.Dtos.Channels; + +/// +/// 问题模板下拉选项。 +/// +public class IssueTemplateOptionOutput +{ + public int Id { get; set; } + + public string IssueType { get; set; } = string.Empty; + + public string? IssueTypeName { get; set; } + + public string Title { get; set; } = string.Empty; +} diff --git a/src/Oms.Application/Dtos/Channels/IssueTemplateOutput.cs b/src/Oms.Application/Dtos/Channels/IssueTemplateOutput.cs new file mode 100644 index 0000000..aa30bb5 --- /dev/null +++ b/src/Oms.Application/Dtos/Channels/IssueTemplateOutput.cs @@ -0,0 +1,31 @@ +namespace Oms.Application.Dtos.Channels; + +/// +/// 问题模板出参。 +/// +public class IssueTemplateOutput +{ + public int Id { get; set; } + + public string? OrganizationId { get; set; } + + public string? OrganizationName { get; set; } + + public string IssueType { get; set; } = string.Empty; + + public string? IssueTypeName { get; set; } + + public string Title { get; set; } = string.Empty; + + public string Content { get; set; } = string.Empty; + + public string NotifyMethod { get; set; } = string.Empty; + + public List NotifyMethods { get; set; } = []; + + public byte Status { get; set; } + + public DateTime CreateTime { get; set; } + + public DateTime? UpdateTime { get; set; } +} diff --git a/src/Oms.Application/Dtos/Channels/IssueTemplateSaveInput.cs b/src/Oms.Application/Dtos/Channels/IssueTemplateSaveInput.cs new file mode 100644 index 0000000..9ae17c6 --- /dev/null +++ b/src/Oms.Application/Dtos/Channels/IssueTemplateSaveInput.cs @@ -0,0 +1,23 @@ +using Oms.Domain.Shared.Enums; + +namespace Oms.Application.Dtos.Channels; + +/// +/// 问题模板保存入参。 +/// +public class IssueTemplateSaveInput +{ + public int? Id { get; set; } + + public string? OrganizationId { get; set; } + + public string? IssueType { get; set; } + + public string? Title { get; set; } + + public string? Content { get; set; } + + public string? NotifyMethod { get; set; } + + public EnableStatus Status { get; set; } = EnableStatus.Enabled; +} diff --git a/src/Oms.Application/Services/Channels/IssueTemplateService.cs b/src/Oms.Application/Services/Channels/IssueTemplateService.cs new file mode 100644 index 0000000..4cb2ed6 --- /dev/null +++ b/src/Oms.Application/Services/Channels/IssueTemplateService.cs @@ -0,0 +1,303 @@ +using System.Linq.Expressions; +using Fake.Application; +using Microsoft.AspNetCore.Mvc; +using Oms.Application.DataPerm; +using Oms.Application.Dtos.Channels; +using Oms.Application.Dtos.Shared; +using Oms.Application.Shared; +using Oms.Domain.ChannelsAggregate.Consts; +using Oms.Domain.ChannelsAggregate.Entities; +using Oms.Domain.ChannelsAggregate.Repositories; +using Oms.Domain.Shared.Enums; + +namespace Oms.Application.Services.Channels; + +/// +/// 问题模板 API 服务。 +/// +[ApiExplorerSettings(GroupName = "OMS-Channel")] +public class IssueTemplateService : ApplicationService +{ + private IIssueTemplateRepository IssueTemplateRepository => + LazyServiceProvider.GetRequiredService(); + + private DataPermRepository DataPermRepository => + LazyServiceProvider.GetRequiredService(); + + [HttpGet("/channel/issue-template/list")] + public async Task> GetListAsync([FromQuery] IssueTemplateListInput input) + { + var predicate = BuildListPredicate(input); + var pageIndex = OmsDocHelper.NormalizePage(input.Page); + var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize); + var entities = await IssueTemplateRepository.GetPagedListAsync( + predicate, + pageIndex, + pageSize, + new Dictionary { [nameof(IssueTemplate.UpdateTime)] = false, [nameof(IssueTemplate.Id)] = false }); + var total = await IssueTemplateRepository.CountAsync(predicate); + var orgNameMap = await ResolveOrganizationNameMapAsync(entities); + var items = entities.Select(x => MapOutput(x, orgNameMap)).ToList(); + return new OmsPageResult { Items = items, Total = total }; + } + + [HttpGet("/channel/issue-template/detail")] + public async Task GetDetailAsync([FromQuery] int id) + { + var entity = await GetEntityOrThrowAsync(id); + var orgNameMap = await ResolveOrganizationNameMapAsync([entity]); + return MapOutput(entity, orgNameMap); + } + + [HttpPost("/channel/issue-template/create")] + public async Task CreateAsync(IssueTemplateSaveInput input) + { + var normalized = await NormalizeSaveInputAsync(input); + var entity = new IssueTemplate + { + OrganizationId = normalized.OrganizationId, + IssueType = normalized.IssueType, + Title = normalized.Title, + Content = normalized.Content, + NotifyMethod = normalized.NotifyMethod, + Status = normalized.Status + }; + await IssueTemplateRepository.InsertAsync(entity); + } + + [HttpPost("/channel/issue-template/update")] + public async Task UpdateAsync(IssueTemplateSaveInput input) + { + var entity = await GetEntityOrThrowAsync(input.Id); + var normalized = await NormalizeSaveInputAsync(input, input.Id); + entity.OrganizationId = normalized.OrganizationId; + entity.IssueType = normalized.IssueType; + entity.Title = normalized.Title; + entity.Content = normalized.Content; + entity.NotifyMethod = normalized.NotifyMethod; + entity.Status = normalized.Status; + await IssueTemplateRepository.UpdateAsync(entity); + } + + [HttpPost("/channel/issue-template/delete")] + public async Task DeleteAsync(IdInput input) + { + var entity = await GetEntityOrThrowAsync(input.Id); + await IssueTemplateRepository.DeleteAsync(entity); + } + + [HttpPost("/channel/issue-template/toggleStatus")] + public async Task ToggleStatusAsync(ToggleStatusInput input) + { + var entity = await GetEntityOrThrowAsync(input.Id); + entity.Status = input.Status; + await IssueTemplateRepository.UpdateAsync(entity); + } + + [HttpGet("/channel/issue-template/options")] + public async Task> GetOptionsAsync( + [FromQuery] string? organizationId = null, + [FromQuery] string? issueType = null) + { + var normalizedOrg = NormalizeOrganizationId(organizationId); + var normalizedType = issueType?.Trim(); + + var entities = await IssueTemplateRepository.GetListAsync(x => + x.Status == EnableStatus.Enabled + && (string.IsNullOrWhiteSpace(normalizedType) || x.IssueType == normalizedType) + && (normalizedOrg == null + || x.OrganizationId == normalizedOrg + || x.OrganizationId == null + || x.OrganizationId == string.Empty)); + + var grouped = entities + .GroupBy(x => x.IssueType, StringComparer.OrdinalIgnoreCase) + .Select(group => + { + var preferred = group.FirstOrDefault(x => x.OrganizationId == normalizedOrg) + ?? group.FirstOrDefault(x => string.IsNullOrWhiteSpace(x.OrganizationId)) + ?? group.First(); + return new IssueTemplateOptionOutput + { + Id = preferred.Id, + IssueType = preferred.IssueType, + IssueTypeName = IssueTypeCodes.GetDisplayName(preferred.IssueType), + Title = preferred.Title + }; + }) + .OrderBy(x => x.IssueType, StringComparer.OrdinalIgnoreCase) + .ToList(); + + return new OmsItemsResult { Items = grouped }; + } + + private static Expression> BuildListPredicate(IssueTemplateListInput input) + { + var organizationId = NormalizeOrganizationId(input.OrganizationId); + var issueType = input.IssueType?.Trim(); + var keyword = input.Keyword?.Trim(); + var status = input.Status; + + return entity => + (organizationId == null || entity.OrganizationId == organizationId) + && (string.IsNullOrWhiteSpace(issueType) || entity.IssueType == issueType) + && (status == null || (byte)entity.Status == status.Value) + && (string.IsNullOrWhiteSpace(keyword) + || entity.Title.Contains(keyword) + || entity.Content.Contains(keyword)); + } + + private async Task NormalizeSaveInputAsync( + IssueTemplateSaveInput input, + int? excludeId = null) + { + var issueType = input.IssueType?.Trim().ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(issueType) || !IssueTypeCodes.All.Contains(issueType)) + { + throw new BusinessException("问题类型不合法"); + } + + var title = input.Title?.Trim(); + if (string.IsNullOrWhiteSpace(title)) + { + throw new BusinessException("问题标题不能为空"); + } + + if (title.Length > 128) + { + throw new BusinessException("问题标题长度不能超过128"); + } + + var content = input.Content?.Trim(); + if (string.IsNullOrWhiteSpace(content)) + { + throw new BusinessException("问题内容不能为空"); + } + + if (content.Length > 5000) + { + throw new BusinessException("问题内容长度不能超过5000"); + } + + var notifyMethod = IssueNotifyMethodCodes.Normalize(input.NotifyMethod); + if (string.IsNullOrWhiteSpace(notifyMethod)) + { + throw new BusinessException("请至少选择一种通知方式"); + } + + var organizationId = NormalizeOrganizationId(input.OrganizationId); + if (!string.IsNullOrWhiteSpace(organizationId)) + { + if (!Guid.TryParse(organizationId, out var orgGuid)) + { + throw new BusinessException("组织ID格式不正确"); + } + + if (!await DataPermRepository.OrganizationExistsAndEnabledAsync(orgGuid)) + { + throw new BusinessException("组织不存在"); + } + } + + if (await IssueTemplateRepository.ExistsDuplicateAsync(organizationId, issueType, excludeId)) + { + throw new BusinessException("同一组织下该问题类型已存在模板"); + } + + return new NormalizedIssueTemplateSaveInput + { + OrganizationId = organizationId, + IssueType = issueType, + Title = title, + Content = content, + NotifyMethod = notifyMethod, + Status = input.Status + }; + } + + private static string? NormalizeOrganizationId(string? organizationId) + { + var normalized = organizationId?.Trim(); + return string.IsNullOrWhiteSpace(normalized) ? null : normalized; + } + + private async Task GetEntityOrThrowAsync(int? id) + { + if (!id.HasValue || id.Value <= 0) + { + throw new BusinessException("问题模板不存在"); + } + + var entity = await IssueTemplateRepository.FirstOrDefaultAsync(x => x.Id == id.Value); + if (entity == null) + { + throw new BusinessException("问题模板不存在"); + } + + return entity; + } + + private async Task> ResolveOrganizationNameMapAsync( + IReadOnlyCollection entities) + { + var orgIds = entities + .Select(x => x.OrganizationId) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Select(x => Guid.TryParse(x, out var guid) ? guid : Guid.Empty) + .Where(x => x != Guid.Empty) + .Distinct() + .ToList(); + + return orgIds.Count == 0 + ? [] + : await DataPermRepository.GetOrganizationNamesAsync(orgIds); + } + + private static IssueTemplateOutput MapOutput( + IssueTemplate entity, + IReadOnlyDictionary orgNameMap) + { + string? organizationName = null; + if (!string.IsNullOrWhiteSpace(entity.OrganizationId) + && Guid.TryParse(entity.OrganizationId, out var orgGuid) + && orgNameMap.TryGetValue(orgGuid, out var name)) + { + organizationName = name; + } + else if (string.IsNullOrWhiteSpace(entity.OrganizationId)) + { + organizationName = "平台通用"; + } + + return new IssueTemplateOutput + { + Id = entity.Id, + OrganizationId = entity.OrganizationId, + OrganizationName = organizationName, + IssueType = entity.IssueType, + IssueTypeName = IssueTypeCodes.GetDisplayName(entity.IssueType), + Title = entity.Title, + Content = entity.Content, + NotifyMethod = entity.NotifyMethod, + NotifyMethods = IssueNotifyMethodCodes.ParseList(entity.NotifyMethod), + Status = (byte)entity.Status, + CreateTime = entity.CreateTime, + UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime) + }; + } + + private sealed class NormalizedIssueTemplateSaveInput + { + public string? OrganizationId { get; set; } + + public string IssueType { get; set; } = string.Empty; + + public string Title { get; set; } = string.Empty; + + public string Content { get; set; } = string.Empty; + + public string NotifyMethod { get; set; } = string.Empty; + + public EnableStatus Status { get; set; } + } +} diff --git a/src/Oms.Domain/ChannelsAggregate/Consts/IssueNotifyMethodCodes.cs b/src/Oms.Domain/ChannelsAggregate/Consts/IssueNotifyMethodCodes.cs new file mode 100644 index 0000000..3d495ec --- /dev/null +++ b/src/Oms.Domain/ChannelsAggregate/Consts/IssueNotifyMethodCodes.cs @@ -0,0 +1,57 @@ +namespace Oms.Domain.ChannelsAggregate.Consts; + +public static class IssueNotifyMethodCodes +{ + public const string InApp = "inApp"; + public const string Email = "email"; + public const string Sms = "sms"; + public const string Feishu = "feishu"; + public const string Wechat = "wechat"; + + public static readonly HashSet All = + [ + InApp, + Email, + Sms, + Feishu, + Wechat + ]; + + public static string GetDisplayName(string? code) => code?.Trim().ToLowerInvariant() switch + { + InApp => "站内信", + Email => "邮件", + Sms => "短信", + Feishu => "飞书", + Wechat => "微信", + _ => code ?? string.Empty + }; + + public static string Normalize(string? raw) + { + if (string.IsNullOrWhiteSpace(raw)) + { + return string.Empty; + } + + var parts = raw + .Split([',', ';', ' '], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Select(x => x.ToLowerInvariant()) + .Where(x => All.Contains(x)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + return string.Join(",", parts); + } + + public static List ParseList(string? raw) + { + var normalized = Normalize(raw); + if (string.IsNullOrWhiteSpace(normalized)) + { + return []; + } + + return normalized.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList(); + } +} diff --git a/src/Oms.Domain/ChannelsAggregate/Consts/IssueTypeCodes.cs b/src/Oms.Domain/ChannelsAggregate/Consts/IssueTypeCodes.cs new file mode 100644 index 0000000..baf5d19 --- /dev/null +++ b/src/Oms.Domain/ChannelsAggregate/Consts/IssueTypeCodes.cs @@ -0,0 +1,38 @@ +namespace Oms.Domain.ChannelsAggregate.Consts; + +public static class IssueTypeCodes +{ + public const string Address = "address"; + public const string Customs = "customs"; + public const string Weight = "weight"; + public const string Document = "document"; + public const string Cargo = "cargo"; + public const string Channel = "channel"; + public const string Payment = "payment"; + public const string Other = "other"; + + public static readonly HashSet All = + [ + Address, + Customs, + Weight, + Document, + Cargo, + Channel, + Payment, + Other + ]; + + public static string GetDisplayName(string? code) => code?.Trim().ToLowerInvariant() switch + { + Address => "地址异常", + Customs => "清关问题", + Weight => "重量异常", + Document => "单证不全", + Cargo => "货物异常", + Channel => "渠道异常", + Payment => "费用异常", + Other => "其他", + _ => code ?? string.Empty + }; +} diff --git a/src/Oms.Domain/ChannelsAggregate/Entities/IssueTemplate.cs b/src/Oms.Domain/ChannelsAggregate/Entities/IssueTemplate.cs new file mode 100644 index 0000000..4a07876 --- /dev/null +++ b/src/Oms.Domain/ChannelsAggregate/Entities/IssueTemplate.cs @@ -0,0 +1,21 @@ +using Oms.Domain.Shared; +using Oms.Domain.Shared.Enums; +using SqlSugar; + +namespace Oms.Domain.ChannelsAggregate.Entities; + +[SugarTable("t_issue_template")] +public class IssueTemplate : OmsAuditedAggregateRoot +{ + public string? OrganizationId { get; set; } + + public string IssueType { get; set; } = string.Empty; + + public string Title { get; set; } = string.Empty; + + public string Content { get; set; } = string.Empty; + + public string NotifyMethod { get; set; } = string.Empty; + + public EnableStatus Status { get; set; } = EnableStatus.Enabled; +} diff --git a/src/Oms.Domain/ChannelsAggregate/Repositories/IIssueTemplateRepository.cs b/src/Oms.Domain/ChannelsAggregate/Repositories/IIssueTemplateRepository.cs new file mode 100644 index 0000000..dd0f7aa --- /dev/null +++ b/src/Oms.Domain/ChannelsAggregate/Repositories/IIssueTemplateRepository.cs @@ -0,0 +1,13 @@ +using Fake.Domain.Repositories; +using Oms.Domain.ChannelsAggregate.Entities; + +namespace Oms.Domain.ChannelsAggregate.Repositories; + +public interface IIssueTemplateRepository : IRepository +{ + Task ExistsDuplicateAsync( + string? organizationId, + string issueType, + int? excludeId = null, + CancellationToken cancellationToken = default); +} diff --git a/src/Oms.SqlSugarCore/Channels/IssueTemplateRepository.cs b/src/Oms.SqlSugarCore/Channels/IssueTemplateRepository.cs new file mode 100644 index 0000000..2dfdd46 --- /dev/null +++ b/src/Oms.SqlSugarCore/Channels/IssueTemplateRepository.cs @@ -0,0 +1,27 @@ +using Fake.DependencyInjection; +using Oms.Domain.ChannelsAggregate.Entities; +using Oms.Domain.ChannelsAggregate.Repositories; +using Oms.SqlSugarCore.Repositories; + +namespace Oms.SqlSugarCore.Channels; + +public class IssueTemplateRepository : OmsSqlSugarRepository, IIssueTemplateRepository, IScopedDependency +{ + public async Task ExistsDuplicateAsync( + string? organizationId, + string issueType, + int? excludeId = null, + CancellationToken cancellationToken = default) + { + var normalizedOrg = string.IsNullOrWhiteSpace(organizationId) ? null : organizationId.Trim(); + var normalizedType = issueType.Trim(); + + return await AnyAsync( + x => + x.IssueType == normalizedType + && ((normalizedOrg == null && (x.OrganizationId == null || x.OrganizationId == string.Empty)) + || x.OrganizationId == normalizedOrg) + && (!excludeId.HasValue || x.Id != excludeId.Value), + cancellationToken); + } +}