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; [ApiExplorerSettings(GroupName = "OMS-Channel")] public partial class ServiceChannelService : ApplicationService { private const string PriceOperatorType = "price"; private const string RulesOperatorType = "rules"; private IServiceChannelRepository ServiceChannelRepository => LazyServiceProvider.GetRequiredService(); /// /// 获取服务渠道分页列表。 /// [HttpGet("/channel/service-channel/list")] public async Task> 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 { [nameof(ServiceChannel.UpdateTime)] = false, [nameof(ServiceChannel.Id)] = false }); var total = await ServiceChannelRepository.CountAsync(predicate); var groupMap = await GetGroupMapAsync(); return new OmsPageResult { Items = entities.Select(entity => MapListOutput(entity, groupMap)).ToList(), Total = total }; } /// /// 获取服务渠道详情。 /// [HttpGet("/channel/service-channel/detail")] public async Task GetDetailAsync([FromQuery] long id) { var entity = await GetEntityOrThrowAsync(id); return MapDetailOutput(entity, await GetGroupMapAsync()); } /// /// 新增服务渠道。 /// [HttpPost("/channel/service-channel/create")] public async Task CreateAsync(ServiceChannelSaveInput input) { await ValidateChannelCodeAsync(input.ChannelCode, null); var entity = new ServiceChannel { IsDeleted = false, CreateTime = DateTime.Now, CreateUserId = CurrentUser.GetId() }; ApplyInput(entity, input); await ServiceChannelRepository.InsertAsync(entity); } /// /// 更新服务渠道。 /// [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); entity.UpdateTime = DateTime.Now; entity.UpdateUserId = CurrentUser.GetId(); await ServiceChannelRepository.UpdateAsync(entity); } /// /// 删除服务渠道。 /// [HttpPost("/channel/service-channel/delete")] public async Task DeleteAsync(IdInput input) { var entity = await GetEntityOrThrowAsync(input.Id); entity.IsDeleted = true; entity.UpdateTime = DateTime.Now; entity.UpdateUserId = CurrentUser.GetId(); await ServiceChannelRepository.UpdateAsync(entity); } /// /// 保存服务渠道配置。 /// [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); entity.UpdateTime = DateTime.Now; entity.UpdateUserId = CurrentUser.GetId(); await ServiceChannelRepository.UpdateAsync(entity); } /// /// 获取服务渠道买价项列表。 /// [HttpGet("/channel/service-channel/price/list")] public async Task> GetPriceListAsync([FromQuery] long serviceChannelId) { await GetEntityOrThrowAsync(serviceChannelId); return new OmsItemsResult { Items = await GetPriceOutputsAsync(serviceChannelId) }; } /// /// 新增服务渠道买价项。 /// [HttpPost("/channel/service-channel/price/create")] public async Task CreatePriceAsync(ServiceChannelPriceSaveInput input) { await GetEntityOrThrowAsync(input.ServiceChannelId); var now = DateTime.Now; var userId = CurrentUser.GetId(); 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, IsDeleted = false, CreateTime = now, CreateUserId = userId }, BuildPriceFactors(input.Factors, now, userId), BuildPriceDetails(input.Details, now, userId)); } /// /// 更新服务渠道买价项。 /// [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; price.UpdateTime = DateTime.Now; price.UpdateUserId = CurrentUser.GetId(); await ServiceChannelRepository.UpdatePriceAsync( price, BuildPriceFactors(input.Factors, DateTime.Now, CurrentUser.GetId()), BuildPriceDetails(input.Details, DateTime.Now, CurrentUser.GetId())); } /// /// 删除服务渠道买价项。 /// [HttpPost("/channel/service-channel/price/delete")] public async Task DeletePriceAsync(IdInput input) { await GetPriceOrThrowAsync(input.Id); await ServiceChannelRepository.DeletePriceAsync(input.Id); } /// /// 切换服务渠道买价项状态。 /// [HttpPost("/channel/service-channel/price/toggleStatus")] public async Task TogglePriceStatusAsync(ToggleStatusInput input) { await GetPriceOrThrowAsync(input.Id); await ServiceChannelRepository.UpdatePriceStatusAsync(input.Id, input.Status); } /// /// 批量保存服务渠道买价项。 /// [HttpPost("/channel/service-channel/price/save")] public async Task SavePriceAsync(ServiceChannelPriceSaveAllInput input) { await GetEntityOrThrowAsync(input.ServiceChannelId); var now = DateTime.Now; var userId = CurrentUser.GetId(); var prices = new List(); var factors = new List(); var details = new List(); var tempId = -1L; // 使用临时负数 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, IsDeleted = false, CreateTime = now, CreateUserId = userId }); factors.AddRange(BuildPriceFactors(priceInput.Factors, now, userId, currentTempId)); details.AddRange(BuildPriceDetails(priceInput.Details, now, userId, currentTempId)); } await ServiceChannelRepository.ReplacePricesAsync(input.ServiceChannelId, prices, factors, details); if (input.Publish) { await CreatePublishRecordAsync(input.ServiceChannelId, PriceOperatorType, "保存并发布买价"); } } /// /// 获取服务渠道价格发布记录。 /// [HttpGet("/channel/service-channel/publish/price/list")] public Task> GetPricePublishListAsync([FromQuery] ServiceChannelPublishListInput input) { return GetPublishListAsync(input, PriceOperatorType); } /// /// 获取服务渠道规则发布记录。 /// [HttpGet("/channel/service-channel/publish/rules/list")] public Task> GetRulesPublishListAsync([FromQuery] ServiceChannelPublishListInput input) { return GetPublishListAsync(input, RulesOperatorType); } /// /// 发布当前服务渠道配置。 /// [HttpPost("/channel/service-channel/publish")] public async Task PublishAsync(ServiceChannelPublishInput input) { await CreatePublishRecordAsync(input.ServiceChannelId, NormalizeOperatorType(input.OperatorType), input.Description); } /// /// 按发布记录回滚服务渠道配置。 /// [HttpPost("/channel/service-channel/publish/rollback")] public Task RollbackAsync(ServiceChannelRollbackInput input) { return RollbackCoreAsync(input); } private static Expression> BuildListPredicate(ServiceChannelListInput input) { var keyword = input.Keyword?.Trim(); return entity => !entity.IsDeleted && (!input.GroupId.HasValue || entity.GroupId == input.GroupId.Value) && (string.IsNullOrWhiteSpace(keyword) || entity.ChannelCode.Contains(keyword) || entity.ChannelName.Contains(keyword)) && (!input.IsActive.HasValue || entity.IsActive == input.IsActive.Value); } }