506 lines
21 KiB
C#
506 lines
21 KiB
C#
using System.Linq.Expressions;
|
||
using Fake.Application;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Oms.Application.Dtos.Channels;
|
||
using Oms.Application.Dtos.Shared;
|
||
using Oms.Application.Shared;
|
||
using Oms.Domain.ChannelsAggregate.Entities;
|
||
using Oms.Domain.ChannelsAggregate.Repositories;
|
||
|
||
namespace Oms.Application.Services.Channels;
|
||
|
||
/// <summary>
|
||
/// 服务渠道 API 服务。
|
||
/// </summary>
|
||
[ApiExplorerSettings(GroupName = "OMS-Channel")]
|
||
public partial class ServiceChannelService : ApplicationService
|
||
{
|
||
private const string PriceOperatorType = "price";
|
||
private const string RulesOperatorType = "rules";
|
||
|
||
private IServiceChannelRepository ServiceChannelRepository => LazyServiceProvider.GetRequiredService<IServiceChannelRepository>();
|
||
private IServiceChannelGroupRepository ServiceChannelGroupRepository => LazyServiceProvider.GetRequiredService<IServiceChannelGroupRepository>();
|
||
|
||
/// <summary>
|
||
/// 查询服务渠道列表。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道列表查询条件。</param>
|
||
/// <returns>服务渠道分页结果,包含列表数据和总记录数。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 根据查询条件构造筛选规则。
|
||
/// 2. 查询目标数据并补齐关联展示字段。
|
||
/// 3. 按接口约定返回列表结果。
|
||
/// </remarks>
|
||
[HttpGet("/channel/service-channel/list")]
|
||
public async Task<OmsPageResult<ServiceChannelListOutput>> GetListAsync([FromQuery] ServiceChannelListInput input)
|
||
{
|
||
var predicate = BuildListPredicate(input);
|
||
var pageIndex = OmsDocHelper.NormalizePage(input.Page);
|
||
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
|
||
var entities = await ServiceChannelRepository.GetPagedListAsync(
|
||
predicate,
|
||
pageIndex,
|
||
pageSize,
|
||
new Dictionary<string, bool> { [nameof(ServiceChannel.UpdateTime)] = false, [nameof(ServiceChannel.Id)] = false });
|
||
var total = await ServiceChannelRepository.CountAsync(predicate);
|
||
var groupMap = await GetGroupMapAsync();
|
||
return new OmsPageResult<ServiceChannelListOutput>
|
||
{
|
||
Items = entities.Select(entity => MapListOutput(entity, groupMap)).ToList(),
|
||
Total = total
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询服务渠道详情。
|
||
/// </summary>
|
||
/// <param name="id">入参:目标主键。</param>
|
||
/// <returns>服务渠道详情。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 根据标识查询目标对象。
|
||
/// 2. 目标不存在时抛出业务异常。
|
||
/// 3. 映射并返回详情结果。
|
||
/// </remarks>
|
||
[HttpGet("/channel/service-channel/detail")]
|
||
public async Task<ServiceChannelDetailOutput> GetDetailAsync([FromQuery] int id)
|
||
{
|
||
var entity = await GetEntityOrThrowAsync(id);
|
||
return MapDetailOutput(entity, await GetGroupMapAsync());
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增服务渠道。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道保存参数。</param>
|
||
/// <returns>无返回体,仅完成新增服务渠道。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 校验入参与关联对象、唯一性等前置条件。
|
||
/// 2. 组装目标实体及相关明细数据。
|
||
/// 3. 写入审计字段并持久化结果。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/create")]
|
||
public async Task CreateAsync(ServiceChannelSaveInput input)
|
||
{
|
||
await ValidateChannelCodeAsync(input.ChannelCode, null);
|
||
var entity = new ServiceChannel();
|
||
ApplyInput(entity, input);
|
||
await ServiceChannelRepository.InsertAsync(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新服务渠道。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道保存参数。</param>
|
||
/// <returns>无返回体,仅完成更新服务渠道。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标对象。
|
||
/// 2. 按入参更新主体数据和关联配置。
|
||
/// 3. 写入更新审计字段并保存结果。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/update")]
|
||
public async Task UpdateAsync(ServiceChannelSaveInput input)
|
||
{
|
||
var entity = await GetEntityOrThrowAsync(input.Id);
|
||
await ValidateChannelCodeAsync(input.ChannelCode, entity.Id);
|
||
ApplyInput(entity, input);
|
||
await ServiceChannelRepository.UpdateAsync(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除服务渠道。
|
||
/// </summary>
|
||
/// <param name="input">入参:目标主键。</param>
|
||
/// <returns>无返回体,仅完成删除服务渠道。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标对象。
|
||
/// 2. 执行软删除或删除关联明细。
|
||
/// 3. 持久化删除结果。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/delete")]
|
||
public async Task DeleteAsync(IdInput input)
|
||
{
|
||
var entity = await GetEntityOrThrowAsync(input.Id);
|
||
await ServiceChannelRepository.DeleteAsync(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询服务渠道分组列表。
|
||
/// </summary>
|
||
/// <param name="input">入参:分组名称、应用 ID、API 服务商 ID 等筛选条件。</param>
|
||
/// <returns>服务渠道分组列表,供下拉与左侧树筛选使用。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 按 keyword、appId、apiServiceProviderId 构造筛选条件。
|
||
/// 2. 查询未删除分组并按 id 倒序返回。
|
||
/// 3. 无数据时返回空数组。
|
||
/// </remarks>
|
||
[HttpGet("/channel/service-channel/group/list")]
|
||
public async Task<List<ServiceChannelGroupListOutput>> GetGroupListAsync([FromQuery] ServiceChannelGroupListInput input)
|
||
{
|
||
var predicate = BuildGroupListPredicate(input);
|
||
var entities = await ServiceChannelGroupRepository.GetListAsync(
|
||
predicate,
|
||
new Dictionary<string, bool> { [nameof(ServiceChannelGroup.Id)] = false });
|
||
return entities.Select(MapGroupListOutput).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增服务渠道分组。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道分组保存参数。</param>
|
||
/// <returns>无返回体,仅完成新增服务渠道分组。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 校验分组名称、应用ID、API服务商ID等基础字段。
|
||
/// 2. 检查分组名称唯一性,避免重复配置。
|
||
/// 3. 保存分组主数据供服务渠道绑定。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/group/create")]
|
||
public async Task CreateGroupAsync(ServiceChannelGroupSaveInput input)
|
||
{
|
||
await ValidateGroupInputAsync(input, null);
|
||
await ServiceChannelGroupRepository.InsertAsync(new ServiceChannelGroup
|
||
{
|
||
Name = input.Name.Trim(),
|
||
AppId = input.AppId,
|
||
ApiServiceProviderId = input.ApiServiceProviderId
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新服务渠道分组。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道分组保存参数。</param>
|
||
/// <returns>无返回体,仅完成更新服务渠道分组。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标分组是否存在。
|
||
/// 2. 检查名称唯一性并更新分组主数据。
|
||
/// 3. 保留既有渠道关联,只更新分组属性。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/group/update")]
|
||
public async Task UpdateGroupAsync(ServiceChannelGroupSaveInput input)
|
||
{
|
||
var group = await GetGroupOrThrowAsync(input.Id);
|
||
await ValidateGroupInputAsync(input, group.Id);
|
||
group.Name = input.Name.Trim();
|
||
group.AppId = input.AppId;
|
||
group.ApiServiceProviderId = input.ApiServiceProviderId;
|
||
await ServiceChannelGroupRepository.UpdateAsync(group);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除服务渠道分组。
|
||
/// </summary>
|
||
/// <param name="input">入参:目标分组主键。</param>
|
||
/// <returns>无返回体,仅完成删除服务渠道分组。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标分组是否存在。
|
||
/// 2. 检查分组下是否仍有关联渠道。
|
||
/// 3. 无关联时执行删除,保留框架软删语义。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/group/delete")]
|
||
public async Task DeleteGroupAsync(IdInput input)
|
||
{
|
||
var group = await GetGroupOrThrowAsync(input.Id);
|
||
if (await ServiceChannelRepository.AnyAsync(x => x.GroupId == group.Id))
|
||
{
|
||
throw new BusinessException("当前分组下仍有关联渠道,无法删除");
|
||
}
|
||
|
||
await ServiceChannelGroupRepository.DeleteAsync(group);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存服务渠道配置。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道保存参数。</param>
|
||
/// <returns>无返回体,仅完成保存服务渠道配置。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标对象。
|
||
/// 2. 按入参更新主体数据和关联配置。
|
||
/// 3. 写入更新审计字段并保存结果。
|
||
/// </remarks>
|
||
[ApiExplorerSettings(IgnoreApi = true)]
|
||
[HttpPost("/channel/service-channel/config/save")]
|
||
public async Task SaveConfigAsync(ServiceChannelSaveInput input)
|
||
{
|
||
var entity = await GetEntityOrThrowAsync(input.Id);
|
||
await ValidateChannelCodeAsync(input.ChannelCode, entity.Id);
|
||
ApplyInput(entity, input);
|
||
await ServiceChannelRepository.UpdateAsync(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询服务渠道价格项列表。
|
||
/// </summary>
|
||
/// <param name="serviceChannelId">入参:serviceChannelId。</param>
|
||
/// <returns>服务渠道价格项列表结果。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 根据查询条件构造筛选规则。
|
||
/// 2. 查询目标数据并补齐关联展示字段。
|
||
/// 3. 按接口约定返回列表结果。
|
||
/// </remarks>
|
||
[HttpGet("/channel/service-channel/price/list")]
|
||
public async Task<OmsItemsResult<ServiceChannelPriceListOutput>> GetPriceListAsync([FromQuery] int serviceChannelId)
|
||
{
|
||
await GetEntityOrThrowAsync(serviceChannelId);
|
||
return new OmsItemsResult<ServiceChannelPriceListOutput>
|
||
{
|
||
Items = await GetPriceOutputsAsync(serviceChannelId)
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增服务渠道价格项。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道价格项保存参数。</param>
|
||
/// <returns>无返回体,仅完成新增服务渠道价格项。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 校验入参与关联对象、唯一性等前置条件。
|
||
/// 2. 组装目标实体及相关明细数据。
|
||
/// 3. 写入审计字段并持久化结果。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/price/create")]
|
||
public async Task CreatePriceAsync(ServiceChannelPriceSaveInput input)
|
||
{
|
||
await GetEntityOrThrowAsync(input.ServiceChannelId);
|
||
await ServiceChannelRepository.InsertPriceAsync(
|
||
new ServiceChannelPrice
|
||
{
|
||
ServiceChannelId = input.ServiceChannelId,
|
||
SerialNumber = input.SerialNumber,
|
||
Status = input.Status,
|
||
PriceName = input.PriceName,
|
||
CostItem = input.CostItem,
|
||
CostUnit = input.CostUnit,
|
||
Currency = input.Currency,
|
||
ChargingTime = input.ChargingTime,
|
||
ChargingCondition = input.ChargingCondition
|
||
},
|
||
BuildPriceFactors(input.Factors),
|
||
BuildPriceDetails(input.Details));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新服务渠道价格项。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道价格项保存参数。</param>
|
||
/// <returns>无返回体,仅完成更新服务渠道价格项。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标对象。
|
||
/// 2. 按入参更新主体数据和关联配置。
|
||
/// 3. 写入更新审计字段并保存结果。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/price/update")]
|
||
public async Task UpdatePriceAsync(ServiceChannelPriceSaveInput input)
|
||
{
|
||
var price = await GetPriceOrThrowAsync(input.Id);
|
||
if (price.ServiceChannelId != input.ServiceChannelId)
|
||
{
|
||
throw new BusinessException("买价项归属不匹配");
|
||
}
|
||
|
||
price.SerialNumber = input.SerialNumber;
|
||
price.Status = input.Status;
|
||
price.PriceName = input.PriceName;
|
||
price.CostItem = input.CostItem;
|
||
price.CostUnit = input.CostUnit;
|
||
price.Currency = input.Currency;
|
||
price.ChargingTime = input.ChargingTime;
|
||
price.ChargingCondition = input.ChargingCondition;
|
||
await ServiceChannelRepository.UpdatePriceAsync(
|
||
price,
|
||
BuildPriceFactors(input.Factors),
|
||
BuildPriceDetails(input.Details));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除服务渠道价格项。
|
||
/// </summary>
|
||
/// <param name="input">入参:目标主键。</param>
|
||
/// <returns>无返回体,仅完成删除服务渠道价格项。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标对象。
|
||
/// 2. 执行软删除或删除关联明细。
|
||
/// 3. 持久化删除结果。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/price/delete")]
|
||
public async Task DeletePriceAsync(IdInput input)
|
||
{
|
||
await GetPriceOrThrowAsync(input.Id);
|
||
await ServiceChannelRepository.DeletePriceAsync(input.Id);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换服务渠道价格项状态。
|
||
/// </summary>
|
||
/// <param name="input">入参:目标主键和状态值。</param>
|
||
/// <returns>无返回体,仅完成切换服务渠道价格项状态。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标对象。
|
||
/// 2. 更新启停状态。
|
||
/// 3. 保存状态变更结果。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/price/toggleStatus")]
|
||
public async Task TogglePriceStatusAsync(ToggleStatusInput input)
|
||
{
|
||
await GetPriceOrThrowAsync(input.Id);
|
||
await ServiceChannelRepository.UpdatePriceStatusAsync(input.Id, input.Status);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量保存服务渠道价格项。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道价格项批量保存参数。</param>
|
||
/// <returns>无返回体,仅完成批量保存服务渠道价格项。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 查询并校验目标对象。
|
||
/// 2. 按入参更新主体数据和关联配置。
|
||
/// 3. 写入更新审计字段并保存结果。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/price/save")]
|
||
public async Task SavePriceAsync(ServiceChannelPriceSaveAllInput input)
|
||
{
|
||
await GetEntityOrThrowAsync(input.ServiceChannelId);
|
||
var prices = new List<ServiceChannelPrice>();
|
||
var factors = new List<ServiceChannelPriceFactor>();
|
||
var details = new List<ServiceChannelPriceDetail>();
|
||
var tempId = -1;
|
||
|
||
// 使用临时负数 Id 保持买价项与子表数据在批量替换时的关联关系。
|
||
foreach (var priceInput in input.Prices.OrderBy(x => x.SerialNumber))
|
||
{
|
||
var currentTempId = tempId--;
|
||
prices.Add(new ServiceChannelPrice
|
||
{
|
||
Id = currentTempId,
|
||
ServiceChannelId = input.ServiceChannelId,
|
||
SerialNumber = priceInput.SerialNumber,
|
||
Status = priceInput.Status,
|
||
PriceName = priceInput.PriceName,
|
||
CostItem = priceInput.CostItem,
|
||
CostUnit = priceInput.CostUnit,
|
||
Currency = priceInput.Currency,
|
||
ChargingTime = priceInput.ChargingTime,
|
||
ChargingCondition = priceInput.ChargingCondition
|
||
});
|
||
factors.AddRange(BuildPriceFactors(priceInput.Factors, currentTempId));
|
||
details.AddRange(BuildPriceDetails(priceInput.Details, currentTempId));
|
||
}
|
||
|
||
await ServiceChannelRepository.ReplacePricesAsync(input.ServiceChannelId, prices, factors, details);
|
||
if (input.Publish)
|
||
{
|
||
await CreatePublishRecordAsync(input.ServiceChannelId, PriceOperatorType, "保存并发布买价");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询服务渠道发布价格列表。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道发布列表查询条件。</param>
|
||
/// <returns>服务渠道发布价格分页结果,包含列表数据和总记录数。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 根据查询条件构造筛选规则。
|
||
/// 2. 查询目标数据并补齐关联展示字段。
|
||
/// 3. 按接口约定返回列表结果。
|
||
/// </remarks>
|
||
[HttpGet("/channel/service-channel/publish/price/list")]
|
||
public Task<OmsPageResult<ServiceChannelPublishRecordOutput>> GetPricePublishListAsync([FromQuery] ServiceChannelPublishListInput input)
|
||
{
|
||
return GetPublishListAsync(input, PriceOperatorType);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询服务渠道发布规则列表。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道发布列表查询条件。</param>
|
||
/// <returns>服务渠道发布规则分页结果,包含列表数据和总记录数。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 根据查询条件构造筛选规则。
|
||
/// 2. 查询目标数据并补齐关联展示字段。
|
||
/// 3. 按接口约定返回列表结果。
|
||
/// </remarks>
|
||
[HttpGet("/channel/service-channel/publish/rules/list")]
|
||
public Task<OmsPageResult<ServiceChannelPublishRecordOutput>> GetRulesPublishListAsync([FromQuery] ServiceChannelPublishListInput input)
|
||
{
|
||
return GetPublishListAsync(input, RulesOperatorType);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行服务渠道发布。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道发布参数。</param>
|
||
/// <returns>无返回体,仅完成服务渠道发布处理。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 校验目标对象和发布前提条件。
|
||
/// 2. 生成、切换或回退发布版本数据。
|
||
/// 3. 保存发布结果并返回处理信息。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/publish")]
|
||
public async Task PublishAsync(ServiceChannelPublishInput input)
|
||
{
|
||
await CreatePublishRecordAsync(input.ServiceChannelId, NormalizeOperatorType(input.OperatorType), input.Description);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 回滚服务渠道发布版本。
|
||
/// </summary>
|
||
/// <param name="input">入参:服务渠道发布回滚参数。</param>
|
||
/// <returns>无返回体,仅完成回滚服务渠道发布版本。</returns>
|
||
/// <remarks>
|
||
/// 核心业务逻辑:
|
||
/// 1. 校验目标对象和发布前提条件。
|
||
/// 2. 生成、切换或回退发布版本数据。
|
||
/// 3. 保存发布结果并返回处理信息。
|
||
/// </remarks>
|
||
[HttpPost("/channel/service-channel/publish/rollback")]
|
||
public Task RollbackAsync(ServiceChannelRollbackInput input)
|
||
{
|
||
return RollbackCoreAsync(input);
|
||
}
|
||
|
||
private static Expression<Func<ServiceChannel, bool>> BuildListPredicate(ServiceChannelListInput input)
|
||
{
|
||
var keyword = input.Keyword?.Trim();
|
||
var hasGroupFilter = input.GroupId.HasValue;
|
||
var groupIdValue = input.GroupId.GetValueOrDefault();
|
||
var hasIsActiveFilter = input.IsActive.HasValue;
|
||
var isActiveValue = input.IsActive.GetValueOrDefault();
|
||
return entity =>
|
||
(!hasGroupFilter || entity.GroupId == groupIdValue) &&
|
||
(string.IsNullOrWhiteSpace(keyword) || entity.ChannelCode.Contains(keyword) || entity.ChannelName.Contains(keyword)) &&
|
||
(!hasIsActiveFilter || entity.IsActive == isActiveValue);
|
||
}
|
||
|
||
private static Expression<Func<ServiceChannelGroup, bool>> BuildGroupListPredicate(ServiceChannelGroupListInput input)
|
||
{
|
||
var keyword = input.Keyword?.Trim();
|
||
var hasAppId = input.AppId.HasValue;
|
||
var appId = input.AppId.GetValueOrDefault();
|
||
var hasApiServiceProviderId = input.ApiServiceProviderId.HasValue;
|
||
var apiServiceProviderId = input.ApiServiceProviderId.GetValueOrDefault();
|
||
return entity =>
|
||
(string.IsNullOrWhiteSpace(keyword) || entity.Name.Contains(keyword)) &&
|
||
(!hasAppId || entity.AppId == appId) &&
|
||
(!hasApiServiceProviderId || entity.ApiServiceProviderId == apiServiceProviderId);
|
||
}
|
||
}
|