Browse Source
1. 删除 StackCoreIMSBindings 模块 - 删除整个 StackCoreIMSBindings 目录及其所有文件 - 绑定关系操作已完全集成到 NetworkStackConfig 中 2. 移除 Stack_CoreIMS_Binding 的 AuditableEntity 继承 - 改为继承 Entity,简化设计 - 移除 CreatedAt 和 UpdatedAt 属性 - 简化 Create 和 Update 方法 3. 修复 Swagger 冲突 - 重命名响应类和命令类中的绑定项类名 - 解决 schemaId 冲突问题 4. 更新相关文件 - 更新配置、接口、实现和响应类 - 保留必要的仓储接口和实现 5. 优化代码结构 - 删除独立的 StackCoreIMSBindingsController - 简化 API 设计,提高一致性feature/x1-web-request
55 changed files with 9902 additions and 1141 deletions
@ -0,0 +1,27 @@ |
|||
namespace CellularManagement.Application.Features.NetworkStackConfigs.Commands.DeleteNetworkStackConfig; |
|||
|
|||
/// <summary>
|
|||
/// 删除网络栈配置响应
|
|||
/// </summary>
|
|||
public class DeleteNetworkStackConfigResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 网络栈配置ID
|
|||
/// </summary>
|
|||
public string NetworkStackConfigId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 网络栈名称
|
|||
/// </summary>
|
|||
public string NetworkStackName { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 删除的栈与核心网/IMS绑定关系数量
|
|||
/// </summary>
|
|||
public int DeletedBindingCount { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 删除时间
|
|||
/// </summary>
|
|||
public DateTime DeletedAt { get; set; } = DateTime.UtcNow; |
|||
} |
@ -1,39 +0,0 @@ |
|||
using CellularManagement.Domain.Common; |
|||
using MediatR; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Commands.CreateStackCoreIMSBinding; |
|||
|
|||
/// <summary>
|
|||
/// 创建栈与核心网/IMS绑定关系命令
|
|||
/// </summary>
|
|||
public class CreateStackCoreIMSBindingCommand : IRequest<OperationResult<CreateStackCoreIMSBindingResponse>> |
|||
{ |
|||
/// <summary>
|
|||
/// 栈ID(外键)
|
|||
/// </summary>
|
|||
[Required(ErrorMessage = "栈ID不能为空")] |
|||
[MaxLength(50, ErrorMessage = "栈ID不能超过50个字符")] |
|||
public string StackId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 索引(主键的一部分)
|
|||
/// </summary>
|
|||
[Required(ErrorMessage = "索引不能为空")] |
|||
[Range(0, int.MaxValue, ErrorMessage = "索引必须大于等于0")] |
|||
public int Index { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 核心网配置ID(外键)
|
|||
/// </summary>
|
|||
[Required(ErrorMessage = "核心网配置ID不能为空")] |
|||
[MaxLength(50, ErrorMessage = "核心网配置ID不能超过50个字符")] |
|||
public string CnId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// IMS配置ID(外键)
|
|||
/// </summary>
|
|||
[Required(ErrorMessage = "IMS配置ID不能为空")] |
|||
[MaxLength(50, ErrorMessage = "IMS配置ID不能超过50个字符")] |
|||
public string ImsId { get; set; } = null!; |
|||
} |
@ -1,95 +0,0 @@ |
|||
using MediatR; |
|||
using Microsoft.Extensions.Logging; |
|||
using CellularManagement.Domain.Common; |
|||
using CellularManagement.Domain.Repositories.NetworkProfile; |
|||
using CellularManagement.Domain.Repositories.Base; |
|||
using CellularManagement.Domain.Services; |
|||
using CellularManagement.Domain.Entities.NetworkProfile; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Commands.CreateStackCoreIMSBinding; |
|||
|
|||
/// <summary>
|
|||
/// 创建栈与核心网/IMS绑定关系命令处理器
|
|||
/// </summary>
|
|||
public class CreateStackCoreIMSBindingCommandHandler : IRequestHandler<CreateStackCoreIMSBindingCommand, OperationResult<CreateStackCoreIMSBindingResponse>> |
|||
{ |
|||
private readonly IStack_CoreIMS_BindingRepository _stackCoreIMSBindingRepository; |
|||
private readonly ILogger<CreateStackCoreIMSBindingCommandHandler> _logger; |
|||
private readonly IUnitOfWork _unitOfWork; |
|||
private readonly ICurrentUserService _currentUserService; |
|||
|
|||
/// <summary>
|
|||
/// 初始化命令处理器
|
|||
/// </summary>
|
|||
public CreateStackCoreIMSBindingCommandHandler( |
|||
IStack_CoreIMS_BindingRepository stackCoreIMSBindingRepository, |
|||
ILogger<CreateStackCoreIMSBindingCommandHandler> logger, |
|||
IUnitOfWork unitOfWork, |
|||
ICurrentUserService currentUserService) |
|||
{ |
|||
_stackCoreIMSBindingRepository = stackCoreIMSBindingRepository; |
|||
_logger = logger; |
|||
_unitOfWork = unitOfWork; |
|||
_currentUserService = currentUserService; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理创建栈与核心网/IMS绑定关系命令
|
|||
/// </summary>
|
|||
public async Task<OperationResult<CreateStackCoreIMSBindingResponse>> Handle(CreateStackCoreIMSBindingCommand request, CancellationToken cancellationToken) |
|||
{ |
|||
try |
|||
{ |
|||
_logger.LogInformation("开始创建栈与核心网/IMS绑定关系,栈ID: {StackId}, 索引: {Index}", request.StackId, request.Index); |
|||
|
|||
// 检查绑定关系是否已存在
|
|||
if (await _stackCoreIMSBindingRepository.StackIdAndIndexExistsAsync(request.StackId, request.Index, cancellationToken)) |
|||
{ |
|||
_logger.LogWarning("栈与核心网/IMS绑定关系已存在,栈ID: {StackId}, 索引: {Index}", request.StackId, request.Index); |
|||
return OperationResult<CreateStackCoreIMSBindingResponse>.CreateFailure($"栈与核心网/IMS绑定关系已存在,栈ID: {request.StackId}, 索引: {request.Index}"); |
|||
} |
|||
|
|||
// 获取当前用户ID
|
|||
var currentUserId = _currentUserService.GetCurrentUserId(); |
|||
if (string.IsNullOrEmpty(currentUserId)) |
|||
{ |
|||
_logger.LogError("无法获取当前用户ID,用户可能未认证"); |
|||
return OperationResult<CreateStackCoreIMSBindingResponse>.CreateFailure("用户未认证,无法创建栈与核心网/IMS绑定关系"); |
|||
} |
|||
|
|||
// 创建栈与核心网/IMS绑定关系实体
|
|||
var binding = Stack_CoreIMS_Binding.Create( |
|||
stackId: request.StackId, |
|||
index: request.Index, |
|||
cnId: request.CnId, |
|||
imsId: request.ImsId, |
|||
createdBy: currentUserId); |
|||
|
|||
// 保存绑定关系
|
|||
await _stackCoreIMSBindingRepository.AddBindingAsync(binding, cancellationToken); |
|||
|
|||
// 保存更改到数据库
|
|||
await _unitOfWork.SaveChangesAsync(cancellationToken); |
|||
|
|||
// 构建响应
|
|||
var response = new CreateStackCoreIMSBindingResponse |
|||
{ |
|||
StackCoreIMSBindingId = binding.Id, |
|||
StackId = binding.StackId, |
|||
Index = binding.Index, |
|||
CnId = binding.CnId, |
|||
ImsId = binding.ImsId, |
|||
CreatedAt = binding.CreatedAt |
|||
}; |
|||
|
|||
_logger.LogInformation("栈与核心网/IMS绑定关系创建成功,绑定ID: {BindingId}, 栈ID: {StackId}, 索引: {Index}", |
|||
binding.Id, binding.StackId, binding.Index); |
|||
return OperationResult<CreateStackCoreIMSBindingResponse>.CreateSuccess(response); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "创建栈与核心网/IMS绑定关系时发生错误,栈ID: {StackId}, 索引: {Index}", request.StackId, request.Index); |
|||
return OperationResult<CreateStackCoreIMSBindingResponse>.CreateFailure($"创建栈与核心网/IMS绑定关系时发生错误: {ex.Message}"); |
|||
} |
|||
} |
|||
} |
@ -1,37 +0,0 @@ |
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Commands.CreateStackCoreIMSBinding; |
|||
|
|||
/// <summary>
|
|||
/// 创建栈与核心网/IMS绑定关系响应
|
|||
/// </summary>
|
|||
public class CreateStackCoreIMSBindingResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 绑定关系ID
|
|||
/// </summary>
|
|||
public string StackCoreIMSBindingId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 栈ID(外键)
|
|||
/// </summary>
|
|||
public string StackId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 索引(主键的一部分)
|
|||
/// </summary>
|
|||
public int Index { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 核心网配置ID(外键)
|
|||
/// </summary>
|
|||
public string CnId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// IMS配置ID(外键)
|
|||
/// </summary>
|
|||
public string ImsId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 创建时间
|
|||
/// </summary>
|
|||
public DateTime CreatedAt { get; set; } |
|||
} |
@ -1,17 +0,0 @@ |
|||
using CellularManagement.Domain.Common; |
|||
using MediatR; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Commands.DeleteStackCoreIMSBinding; |
|||
|
|||
/// <summary>
|
|||
/// 删除栈与核心网/IMS绑定关系命令
|
|||
/// </summary>
|
|||
public class DeleteStackCoreIMSBindingCommand : IRequest<OperationResult<bool>> |
|||
{ |
|||
/// <summary>
|
|||
/// 绑定关系ID
|
|||
/// </summary>
|
|||
[Required] |
|||
public string StackCoreIMSBindingId { get; set; } = null!; |
|||
} |
@ -1,64 +0,0 @@ |
|||
using MediatR; |
|||
using Microsoft.Extensions.Logging; |
|||
using CellularManagement.Domain.Common; |
|||
using CellularManagement.Domain.Repositories.NetworkProfile; |
|||
using CellularManagement.Domain.Repositories.Base; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Commands.DeleteStackCoreIMSBinding; |
|||
|
|||
/// <summary>
|
|||
/// 删除栈与核心网/IMS绑定关系命令处理器
|
|||
/// </summary>
|
|||
public class DeleteStackCoreIMSBindingCommandHandler : IRequestHandler<DeleteStackCoreIMSBindingCommand, OperationResult<bool>> |
|||
{ |
|||
private readonly IStack_CoreIMS_BindingRepository _stackCoreIMSBindingRepository; |
|||
private readonly ILogger<DeleteStackCoreIMSBindingCommandHandler> _logger; |
|||
private readonly IUnitOfWork _unitOfWork; |
|||
|
|||
/// <summary>
|
|||
/// 初始化命令处理器
|
|||
/// </summary>
|
|||
public DeleteStackCoreIMSBindingCommandHandler( |
|||
IStack_CoreIMS_BindingRepository stackCoreIMSBindingRepository, |
|||
ILogger<DeleteStackCoreIMSBindingCommandHandler> logger, |
|||
IUnitOfWork unitOfWork) |
|||
{ |
|||
_stackCoreIMSBindingRepository = stackCoreIMSBindingRepository; |
|||
_logger = logger; |
|||
_unitOfWork = unitOfWork; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理删除栈与核心网/IMS绑定关系命令
|
|||
/// </summary>
|
|||
public async Task<OperationResult<bool>> Handle(DeleteStackCoreIMSBindingCommand request, CancellationToken cancellationToken) |
|||
{ |
|||
try |
|||
{ |
|||
_logger.LogInformation("开始删除栈与核心网/IMS绑定关系,绑定ID: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
|
|||
// 检查绑定关系是否存在
|
|||
var existingBinding = await _stackCoreIMSBindingRepository.GetBindingByIdAsync(request.StackCoreIMSBindingId, cancellationToken); |
|||
if (existingBinding == null) |
|||
{ |
|||
_logger.LogWarning("栈与核心网/IMS绑定关系不存在: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
return OperationResult<bool>.CreateFailure($"栈与核心网/IMS绑定关系 {request.StackCoreIMSBindingId} 不存在"); |
|||
} |
|||
|
|||
// 删除绑定关系
|
|||
await _stackCoreIMSBindingRepository.DeleteBindingAsync(request.StackCoreIMSBindingId, cancellationToken); |
|||
|
|||
// 保存更改到数据库
|
|||
await _unitOfWork.SaveChangesAsync(cancellationToken); |
|||
|
|||
_logger.LogInformation("栈与核心网/IMS绑定关系删除成功,绑定ID: {StackCoreIMSBindingId}, 栈ID: {StackId}, 索引: {Index}", |
|||
request.StackCoreIMSBindingId, existingBinding.StackId, existingBinding.Index); |
|||
return OperationResult<bool>.CreateSuccess(true); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "删除栈与核心网/IMS绑定关系时发生错误,绑定ID: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
return OperationResult<bool>.CreateFailure($"删除栈与核心网/IMS绑定关系时发生错误: {ex.Message}"); |
|||
} |
|||
} |
|||
} |
@ -1,38 +0,0 @@ |
|||
using CellularManagement.Domain.Common; |
|||
using MediatR; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Commands.UpdateStackCoreIMSBinding; |
|||
|
|||
/// <summary>
|
|||
/// 更新栈与核心网/IMS绑定关系命令
|
|||
/// </summary>
|
|||
public class UpdateStackCoreIMSBindingCommand : IRequest<OperationResult<UpdateStackCoreIMSBindingResponse>> |
|||
{ |
|||
/// <summary>
|
|||
/// 绑定关系ID
|
|||
/// </summary>
|
|||
[Required] |
|||
public string StackCoreIMSBindingId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 索引(主键的一部分)
|
|||
/// </summary>
|
|||
[Required(ErrorMessage = "索引不能为空")] |
|||
[Range(0, int.MaxValue, ErrorMessage = "索引必须大于等于0")] |
|||
public int Index { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 核心网配置ID(外键)
|
|||
/// </summary>
|
|||
[Required(ErrorMessage = "核心网配置ID不能为空")] |
|||
[MaxLength(50, ErrorMessage = "核心网配置ID不能超过50个字符")] |
|||
public string CnId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// IMS配置ID(外键)
|
|||
/// </summary>
|
|||
[Required(ErrorMessage = "IMS配置ID不能为空")] |
|||
[MaxLength(50, ErrorMessage = "IMS配置ID不能超过50个字符")] |
|||
public string ImsId { get; set; } = null!; |
|||
} |
@ -1,103 +0,0 @@ |
|||
using MediatR; |
|||
using Microsoft.Extensions.Logging; |
|||
using CellularManagement.Domain.Common; |
|||
using CellularManagement.Domain.Repositories.NetworkProfile; |
|||
using CellularManagement.Domain.Repositories.Base; |
|||
using CellularManagement.Domain.Services; |
|||
using CellularManagement.Domain.Entities.NetworkProfile; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Commands.UpdateStackCoreIMSBinding; |
|||
|
|||
/// <summary>
|
|||
/// 更新栈与核心网/IMS绑定关系命令处理器
|
|||
/// </summary>
|
|||
public class UpdateStackCoreIMSBindingCommandHandler : IRequestHandler<UpdateStackCoreIMSBindingCommand, OperationResult<UpdateStackCoreIMSBindingResponse>> |
|||
{ |
|||
private readonly IStack_CoreIMS_BindingRepository _stackCoreIMSBindingRepository; |
|||
private readonly ILogger<UpdateStackCoreIMSBindingCommandHandler> _logger; |
|||
private readonly IUnitOfWork _unitOfWork; |
|||
private readonly ICurrentUserService _currentUserService; |
|||
|
|||
/// <summary>
|
|||
/// 初始化命令处理器
|
|||
/// </summary>
|
|||
public UpdateStackCoreIMSBindingCommandHandler( |
|||
IStack_CoreIMS_BindingRepository stackCoreIMSBindingRepository, |
|||
ILogger<UpdateStackCoreIMSBindingCommandHandler> logger, |
|||
IUnitOfWork unitOfWork, |
|||
ICurrentUserService currentUserService) |
|||
{ |
|||
_stackCoreIMSBindingRepository = stackCoreIMSBindingRepository; |
|||
_logger = logger; |
|||
_unitOfWork = unitOfWork; |
|||
_currentUserService = currentUserService; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理更新栈与核心网/IMS绑定关系命令
|
|||
/// </summary>
|
|||
public async Task<OperationResult<UpdateStackCoreIMSBindingResponse>> Handle(UpdateStackCoreIMSBindingCommand request, CancellationToken cancellationToken) |
|||
{ |
|||
try |
|||
{ |
|||
_logger.LogInformation("开始更新栈与核心网/IMS绑定关系,绑定ID: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
|
|||
// 检查绑定关系是否存在
|
|||
var existingBinding = await _stackCoreIMSBindingRepository.GetBindingByIdAsync(request.StackCoreIMSBindingId, cancellationToken); |
|||
if (existingBinding == null) |
|||
{ |
|||
_logger.LogWarning("栈与核心网/IMS绑定关系不存在: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
return OperationResult<UpdateStackCoreIMSBindingResponse>.CreateFailure($"栈与核心网/IMS绑定关系 {request.StackCoreIMSBindingId} 不存在"); |
|||
} |
|||
|
|||
// 检查新索引是否与其他绑定关系冲突
|
|||
if (request.Index != existingBinding.Index) |
|||
{ |
|||
if (await _stackCoreIMSBindingRepository.StackIdAndIndexExistsAsync(existingBinding.StackId, request.Index, cancellationToken)) |
|||
{ |
|||
_logger.LogWarning("栈与核心网/IMS绑定关系索引已存在,栈ID: {StackId}, 索引: {Index}", existingBinding.StackId, request.Index); |
|||
return OperationResult<UpdateStackCoreIMSBindingResponse>.CreateFailure($"栈与核心网/IMS绑定关系索引已存在,栈ID: {existingBinding.StackId}, 索引: {request.Index}"); |
|||
} |
|||
} |
|||
|
|||
// 获取当前用户ID
|
|||
var currentUserId = _currentUserService.GetCurrentUserId(); |
|||
if (string.IsNullOrEmpty(currentUserId)) |
|||
{ |
|||
_logger.LogError("无法获取当前用户ID,用户可能未认证"); |
|||
return OperationResult<UpdateStackCoreIMSBindingResponse>.CreateFailure("用户未认证,无法更新栈与核心网/IMS绑定关系"); |
|||
} |
|||
|
|||
// 更新绑定关系
|
|||
existingBinding.Update( |
|||
index: request.Index, |
|||
cnId: request.CnId, |
|||
imsId: request.ImsId, |
|||
updatedBy: currentUserId); |
|||
|
|||
// 保存更改
|
|||
_stackCoreIMSBindingRepository.UpdateBinding(existingBinding); |
|||
await _unitOfWork.SaveChangesAsync(cancellationToken); |
|||
|
|||
// 构建响应
|
|||
var response = new UpdateStackCoreIMSBindingResponse |
|||
{ |
|||
StackCoreIMSBindingId = existingBinding.Id, |
|||
StackId = existingBinding.StackId, |
|||
Index = existingBinding.Index, |
|||
CnId = existingBinding.CnId, |
|||
ImsId = existingBinding.ImsId, |
|||
UpdatedAt = existingBinding.UpdatedAt |
|||
}; |
|||
|
|||
_logger.LogInformation("栈与核心网/IMS绑定关系更新成功,绑定ID: {BindingId}, 栈ID: {StackId}, 索引: {Index}", |
|||
existingBinding.Id, existingBinding.StackId, existingBinding.Index); |
|||
return OperationResult<UpdateStackCoreIMSBindingResponse>.CreateSuccess(response); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "更新栈与核心网/IMS绑定关系时发生错误,绑定ID: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
return OperationResult<UpdateStackCoreIMSBindingResponse>.CreateFailure($"更新栈与核心网/IMS绑定关系时发生错误: {ex.Message}"); |
|||
} |
|||
} |
|||
} |
@ -1,37 +0,0 @@ |
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Commands.UpdateStackCoreIMSBinding; |
|||
|
|||
/// <summary>
|
|||
/// 更新栈与核心网/IMS绑定关系响应
|
|||
/// </summary>
|
|||
public class UpdateStackCoreIMSBindingResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 绑定关系ID
|
|||
/// </summary>
|
|||
public string StackCoreIMSBindingId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 栈ID(外键)
|
|||
/// </summary>
|
|||
public string StackId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 索引(主键的一部分)
|
|||
/// </summary>
|
|||
public int Index { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 核心网配置ID(外键)
|
|||
/// </summary>
|
|||
public string CnId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// IMS配置ID(外键)
|
|||
/// </summary>
|
|||
public string ImsId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 更新时间
|
|||
/// </summary>
|
|||
public DateTime? UpdatedAt { get; set; } |
|||
} |
@ -1,17 +0,0 @@ |
|||
using CellularManagement.Domain.Common; |
|||
using MediatR; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Queries.GetStackCoreIMSBindingById; |
|||
|
|||
/// <summary>
|
|||
/// 根据ID获取栈与核心网/IMS绑定关系查询
|
|||
/// </summary>
|
|||
public class GetStackCoreIMSBindingByIdQuery : IRequest<OperationResult<GetStackCoreIMSBindingByIdResponse>> |
|||
{ |
|||
/// <summary>
|
|||
/// 绑定关系ID
|
|||
/// </summary>
|
|||
[Required] |
|||
public string StackCoreIMSBindingId { get; set; } = null!; |
|||
} |
@ -1,68 +0,0 @@ |
|||
using MediatR; |
|||
using Microsoft.Extensions.Logging; |
|||
using CellularManagement.Domain.Common; |
|||
using CellularManagement.Domain.Repositories.NetworkProfile; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Queries.GetStackCoreIMSBindingById; |
|||
|
|||
/// <summary>
|
|||
/// 根据ID获取栈与核心网/IMS绑定关系查询处理器
|
|||
/// </summary>
|
|||
public class GetStackCoreIMSBindingByIdQueryHandler : IRequestHandler<GetStackCoreIMSBindingByIdQuery, OperationResult<GetStackCoreIMSBindingByIdResponse>> |
|||
{ |
|||
private readonly IStack_CoreIMS_BindingRepository _stackCoreIMSBindingRepository; |
|||
private readonly ILogger<GetStackCoreIMSBindingByIdQueryHandler> _logger; |
|||
|
|||
/// <summary>
|
|||
/// 初始化查询处理器
|
|||
/// </summary>
|
|||
public GetStackCoreIMSBindingByIdQueryHandler( |
|||
IStack_CoreIMS_BindingRepository stackCoreIMSBindingRepository, |
|||
ILogger<GetStackCoreIMSBindingByIdQueryHandler> logger) |
|||
{ |
|||
_stackCoreIMSBindingRepository = stackCoreIMSBindingRepository; |
|||
_logger = logger; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理根据ID获取栈与核心网/IMS绑定关系查询
|
|||
/// </summary>
|
|||
public async Task<OperationResult<GetStackCoreIMSBindingByIdResponse>> Handle(GetStackCoreIMSBindingByIdQuery request, CancellationToken cancellationToken) |
|||
{ |
|||
try |
|||
{ |
|||
_logger.LogInformation("开始获取栈与核心网/IMS绑定关系,绑定ID: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
|
|||
// 获取绑定关系
|
|||
var binding = await _stackCoreIMSBindingRepository.GetBindingByIdAsync(request.StackCoreIMSBindingId, cancellationToken); |
|||
if (binding == null) |
|||
{ |
|||
_logger.LogWarning("栈与核心网/IMS绑定关系不存在: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
return OperationResult<GetStackCoreIMSBindingByIdResponse>.CreateFailure($"栈与核心网/IMS绑定关系 {request.StackCoreIMSBindingId} 不存在"); |
|||
} |
|||
|
|||
// 构建响应
|
|||
var response = new GetStackCoreIMSBindingByIdResponse |
|||
{ |
|||
StackCoreIMSBindingId = binding.Id, |
|||
StackId = binding.StackId, |
|||
Index = binding.Index, |
|||
CnId = binding.CnId, |
|||
ImsId = binding.ImsId, |
|||
CreatedAt = binding.CreatedAt, |
|||
UpdatedAt = binding.UpdatedAt, |
|||
CreatedBy = binding.CreatedBy, |
|||
UpdatedBy = binding.UpdatedBy |
|||
}; |
|||
|
|||
_logger.LogInformation("成功获取栈与核心网/IMS绑定关系,绑定ID: {StackCoreIMSBindingId}, 栈ID: {StackId}, 索引: {Index}", |
|||
binding.Id, binding.StackId, binding.Index); |
|||
return OperationResult<GetStackCoreIMSBindingByIdResponse>.CreateSuccess(response); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "获取栈与核心网/IMS绑定关系时发生错误,绑定ID: {StackCoreIMSBindingId}", request.StackCoreIMSBindingId); |
|||
return OperationResult<GetStackCoreIMSBindingByIdResponse>.CreateFailure($"获取栈与核心网/IMS绑定关系时发生错误: {ex.Message}"); |
|||
} |
|||
} |
|||
} |
@ -1,52 +0,0 @@ |
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Queries.GetStackCoreIMSBindingById; |
|||
|
|||
/// <summary>
|
|||
/// 根据ID获取栈与核心网/IMS绑定关系响应
|
|||
/// </summary>
|
|||
public class GetStackCoreIMSBindingByIdResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 绑定关系ID
|
|||
/// </summary>
|
|||
public string StackCoreIMSBindingId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 栈ID(外键)
|
|||
/// </summary>
|
|||
public string StackId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 索引(主键的一部分)
|
|||
/// </summary>
|
|||
public int Index { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 核心网配置ID(外键)
|
|||
/// </summary>
|
|||
public string CnId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// IMS配置ID(外键)
|
|||
/// </summary>
|
|||
public string ImsId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 创建时间
|
|||
/// </summary>
|
|||
public DateTime CreatedAt { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 更新时间
|
|||
/// </summary>
|
|||
public DateTime? UpdatedAt { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 创建者
|
|||
/// </summary>
|
|||
public string CreatedBy { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 更新者
|
|||
/// </summary>
|
|||
public string UpdatedBy { get; set; } = null!; |
|||
} |
@ -1,41 +0,0 @@ |
|||
using CellularManagement.Domain.Common; |
|||
using MediatR; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Queries.GetStackCoreIMSBindings; |
|||
|
|||
/// <summary>
|
|||
/// 获取栈与核心网/IMS绑定关系列表查询
|
|||
/// </summary>
|
|||
public class GetStackCoreIMSBindingsQuery : IRequest<OperationResult<GetStackCoreIMSBindingsResponse>> |
|||
{ |
|||
/// <summary>
|
|||
/// 页码
|
|||
/// </summary>
|
|||
[Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")] |
|||
public int PageNumber { get; set; } = 1; |
|||
|
|||
/// <summary>
|
|||
/// 每页数量
|
|||
/// </summary>
|
|||
[Range(1, 100, ErrorMessage = "每页数量必须在1-100之间")] |
|||
public int PageSize { get; set; } = 10; |
|||
|
|||
/// <summary>
|
|||
/// 栈ID(可选过滤条件)
|
|||
/// </summary>
|
|||
[MaxLength(50)] |
|||
public string? StackId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 核心网配置ID(可选过滤条件)
|
|||
/// </summary>
|
|||
[MaxLength(50)] |
|||
public string? CnId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// IMS配置ID(可选过滤条件)
|
|||
/// </summary>
|
|||
[MaxLength(50)] |
|||
public string? ImsId { get; set; } |
|||
} |
@ -1,80 +0,0 @@ |
|||
using MediatR; |
|||
using Microsoft.Extensions.Logging; |
|||
using CellularManagement.Domain.Common; |
|||
using CellularManagement.Domain.Repositories.NetworkProfile; |
|||
|
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Queries.GetStackCoreIMSBindings; |
|||
|
|||
/// <summary>
|
|||
/// 获取栈与核心网/IMS绑定关系列表查询处理器
|
|||
/// </summary>
|
|||
public class GetStackCoreIMSBindingsQueryHandler : IRequestHandler<GetStackCoreIMSBindingsQuery, OperationResult<GetStackCoreIMSBindingsResponse>> |
|||
{ |
|||
private readonly IStack_CoreIMS_BindingRepository _stackCoreIMSBindingRepository; |
|||
private readonly ILogger<GetStackCoreIMSBindingsQueryHandler> _logger; |
|||
|
|||
/// <summary>
|
|||
/// 初始化查询处理器
|
|||
/// </summary>
|
|||
public GetStackCoreIMSBindingsQueryHandler( |
|||
IStack_CoreIMS_BindingRepository stackCoreIMSBindingRepository, |
|||
ILogger<GetStackCoreIMSBindingsQueryHandler> logger) |
|||
{ |
|||
_stackCoreIMSBindingRepository = stackCoreIMSBindingRepository; |
|||
_logger = logger; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理获取栈与核心网/IMS绑定关系列表查询
|
|||
/// </summary>
|
|||
public async Task<OperationResult<GetStackCoreIMSBindingsResponse>> Handle(GetStackCoreIMSBindingsQuery request, CancellationToken cancellationToken) |
|||
{ |
|||
try |
|||
{ |
|||
_logger.LogInformation("开始获取栈与核心网/IMS绑定关系列表,页码: {PageNumber}, 每页数量: {PageSize}, 栈ID: {StackId}, 核心网配置ID: {CnId}, IMS配置ID: {ImsId}", |
|||
request.PageNumber, request.PageSize, request.StackId ?? "无", request.CnId ?? "无", request.ImsId ?? "无"); |
|||
|
|||
// 获取绑定关系列表
|
|||
var (totalCount, items) = await _stackCoreIMSBindingRepository.SearchBindingsAsync( |
|||
request.StackId, |
|||
request.CnId, |
|||
request.ImsId, |
|||
request.PageNumber, |
|||
request.PageSize, |
|||
cancellationToken); |
|||
|
|||
// 计算总页数
|
|||
var totalPages = (int)Math.Ceiling((double)totalCount / request.PageSize); |
|||
|
|||
// 构建响应
|
|||
var response = new GetStackCoreIMSBindingsResponse |
|||
{ |
|||
TotalCount = totalCount, |
|||
PageNumber = request.PageNumber, |
|||
PageSize = request.PageSize, |
|||
TotalPages = totalPages, |
|||
Items = items.Select(item => new StackCoreIMSBindingDto |
|||
{ |
|||
StackCoreIMSBindingId = item.Id, |
|||
StackId = item.StackId, |
|||
Index = item.Index, |
|||
CnId = item.CnId, |
|||
ImsId = item.ImsId, |
|||
CreatedAt = item.CreatedAt, |
|||
UpdatedAt = item.UpdatedAt, |
|||
CreatedBy = item.CreatedBy, |
|||
UpdatedBy = item.UpdatedBy |
|||
}).ToList() |
|||
}; |
|||
|
|||
_logger.LogInformation("成功获取栈与核心网/IMS绑定关系列表,总数量: {TotalCount}, 当前页数量: {ItemCount}", |
|||
totalCount, items.Count); |
|||
return OperationResult<GetStackCoreIMSBindingsResponse>.CreateSuccess(response); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "获取栈与核心网/IMS绑定关系列表时发生错误"); |
|||
return OperationResult<GetStackCoreIMSBindingsResponse>.CreateFailure($"获取栈与核心网/IMS绑定关系列表时发生错误: {ex.Message}"); |
|||
} |
|||
} |
|||
} |
@ -1,83 +0,0 @@ |
|||
namespace CellularManagement.Application.Features.StackCoreIMSBindings.Queries.GetStackCoreIMSBindings; |
|||
|
|||
/// <summary>
|
|||
/// 获取栈与核心网/IMS绑定关系列表响应
|
|||
/// </summary>
|
|||
public class GetStackCoreIMSBindingsResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 总数量
|
|||
/// </summary>
|
|||
public int TotalCount { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 当前页码
|
|||
/// </summary>
|
|||
public int PageNumber { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 每页数量
|
|||
/// </summary>
|
|||
public int PageSize { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 总页数
|
|||
/// </summary>
|
|||
public int TotalPages { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 栈与核心网/IMS绑定关系列表
|
|||
/// </summary>
|
|||
public List<StackCoreIMSBindingDto> Items { get; set; } = new(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 栈与核心网/IMS绑定关系数据传输对象
|
|||
/// </summary>
|
|||
public class StackCoreIMSBindingDto |
|||
{ |
|||
/// <summary>
|
|||
/// 绑定关系ID
|
|||
/// </summary>
|
|||
public string StackCoreIMSBindingId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 栈ID(外键)
|
|||
/// </summary>
|
|||
public string StackId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 索引(主键的一部分)
|
|||
/// </summary>
|
|||
public int Index { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 核心网配置ID(外键)
|
|||
/// </summary>
|
|||
public string CnId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// IMS配置ID(外键)
|
|||
/// </summary>
|
|||
public string ImsId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 创建时间
|
|||
/// </summary>
|
|||
public DateTime CreatedAt { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 更新时间
|
|||
/// </summary>
|
|||
public DateTime? UpdatedAt { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 创建者
|
|||
/// </summary>
|
|||
public string CreatedBy { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 更新者
|
|||
/// </summary>
|
|||
public string UpdatedBy { get; set; } = null!; |
|||
} |
@ -1,136 +0,0 @@ |
|||
using CellularManagement.Application.Features.StackCoreIMSBindings.Commands.CreateStackCoreIMSBinding; |
|||
using CellularManagement.Application.Features.StackCoreIMSBindings.Commands.DeleteStackCoreIMSBinding; |
|||
using CellularManagement.Application.Features.StackCoreIMSBindings.Commands.UpdateStackCoreIMSBinding; |
|||
using CellularManagement.Application.Features.StackCoreIMSBindings.Queries.GetStackCoreIMSBindingById; |
|||
using CellularManagement.Application.Features.StackCoreIMSBindings.Queries.GetStackCoreIMSBindings; |
|||
using CellularManagement.Domain.Common; |
|||
using CellularManagement.Presentation.Abstractions; |
|||
using MediatR; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace CellularManagement.Presentation.Controllers; |
|||
|
|||
/// <summary>
|
|||
/// 栈核心网IMS绑定管理控制器
|
|||
/// </summary>
|
|||
[Route("api/stackcoreimsbindings")] |
|||
[ApiController] |
|||
[Authorize] |
|||
public class StackCoreIMSBindingsController : ApiController |
|||
{ |
|||
private readonly ILogger<StackCoreIMSBindingsController> _logger; |
|||
|
|||
/// <summary>
|
|||
/// 初始化栈核心网IMS绑定控制器
|
|||
/// </summary>
|
|||
public StackCoreIMSBindingsController(IMediator mediator, ILogger<StackCoreIMSBindingsController> logger) |
|||
: base(mediator) |
|||
{ |
|||
_logger = logger; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取栈核心网IMS绑定列表
|
|||
/// </summary>
|
|||
[HttpGet] |
|||
public async Task<OperationResult<GetStackCoreIMSBindingsResponse>> GetAll([FromQuery] GetStackCoreIMSBindingsQuery query) |
|||
{ |
|||
_logger.LogInformation("开始获取栈核心网IMS绑定列表,页码: {PageNumber}, 每页数量: {PageSize}, 栈ID: {StackId}, 核心网ID: {CnId}, IMS ID: {ImsId}", |
|||
query.PageNumber, query.PageSize, query.StackId, query.CnId, query.ImsId); |
|||
|
|||
var result = await mediator.Send(query); |
|||
if (!result.IsSuccess) |
|||
{ |
|||
_logger.LogWarning("获取栈核心网IMS绑定列表失败: {Message}", result.ErrorMessages); |
|||
return result; |
|||
} |
|||
|
|||
_logger.LogInformation("成功获取栈核心网IMS绑定列表,共 {Count} 条记录", result.Data?.TotalCount ?? 0); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取栈核心网IMS绑定详情
|
|||
/// </summary>
|
|||
[HttpGet("{id}")] |
|||
public async Task<OperationResult<GetStackCoreIMSBindingByIdResponse>> GetById(string id) |
|||
{ |
|||
_logger.LogInformation("开始获取栈核心网IMS绑定详情,绑定ID: {StackCoreIMSBindingId}", id); |
|||
|
|||
var result = await mediator.Send(new GetStackCoreIMSBindingByIdQuery { StackCoreIMSBindingId = id }); |
|||
if (!result.IsSuccess) |
|||
{ |
|||
_logger.LogWarning("获取栈核心网IMS绑定详情失败: {Message}", result.ErrorMessages); |
|||
return result; |
|||
} |
|||
|
|||
_logger.LogInformation("成功获取栈核心网IMS绑定详情,绑定ID: {StackCoreIMSBindingId}", id); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 创建栈核心网IMS绑定
|
|||
/// </summary>
|
|||
[HttpPost] |
|||
public async Task<OperationResult<CreateStackCoreIMSBindingResponse>> Create([FromBody] CreateStackCoreIMSBindingCommand command) |
|||
{ |
|||
_logger.LogInformation("开始创建栈核心网IMS绑定,栈ID: {StackId}, 索引: {Index}, 核心网ID: {CnId}, IMS ID: {ImsId}", |
|||
command.StackId, command.Index, command.CnId, command.ImsId); |
|||
|
|||
var result = await mediator.Send(command); |
|||
if (!result.IsSuccess) |
|||
{ |
|||
_logger.LogWarning("创建栈核心网IMS绑定失败: {Message}", result.ErrorMessages); |
|||
return result; |
|||
} |
|||
|
|||
_logger.LogInformation("成功创建栈核心网IMS绑定,绑定ID: {StackCoreIMSBindingId}", result.Data?.StackCoreIMSBindingId); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 更新栈核心网IMS绑定
|
|||
/// </summary>
|
|||
[HttpPut("{id}")] |
|||
public async Task<OperationResult<UpdateStackCoreIMSBindingResponse>> Update(string id, [FromBody] UpdateStackCoreIMSBindingCommand command) |
|||
{ |
|||
_logger.LogInformation("开始更新栈核心网IMS绑定,绑定ID: {StackCoreIMSBindingId}", id); |
|||
|
|||
if (id != command.StackCoreIMSBindingId) |
|||
{ |
|||
_logger.LogWarning("栈核心网IMS绑定ID不匹配,路径ID: {PathId}, 命令ID: {CommandId}", id, command.StackCoreIMSBindingId); |
|||
return OperationResult<UpdateStackCoreIMSBindingResponse>.CreateFailure("栈核心网IMS绑定ID不匹配"); |
|||
} |
|||
|
|||
var result = await mediator.Send(command); |
|||
if (!result.IsSuccess) |
|||
{ |
|||
_logger.LogWarning("更新栈核心网IMS绑定失败: {Message}", result.ErrorMessages); |
|||
return result; |
|||
} |
|||
|
|||
_logger.LogInformation("成功更新栈核心网IMS绑定,绑定ID: {StackCoreIMSBindingId}", id); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 删除栈核心网IMS绑定
|
|||
/// </summary>
|
|||
[HttpDelete("{id}")] |
|||
public async Task<OperationResult<bool>> Delete(string id) |
|||
{ |
|||
_logger.LogInformation("开始删除栈核心网IMS绑定,绑定ID: {StackCoreIMSBindingId}", id); |
|||
|
|||
var result = await mediator.Send(new DeleteStackCoreIMSBindingCommand { StackCoreIMSBindingId = id }); |
|||
if (!result.IsSuccess) |
|||
{ |
|||
_logger.LogWarning("删除栈核心网IMS绑定失败: {Message}", result.ErrorMessages); |
|||
return result; |
|||
} |
|||
|
|||
_logger.LogInformation("成功删除栈核心网IMS绑定,绑定ID: {StackCoreIMSBindingId}", id); |
|||
return result; |
|||
} |
|||
} |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,75 @@ |
|||
2025-07-29 00:29:19.787 +08:00 [ERR] DESKTOP-1Q3GI6C [10] An unhandled exception has occurred while executing the request. |
|||
Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Failed to generate Operation for action - CellularManagement.Presentation.Controllers.NetworkStackConfigsController.Create (X1.Presentation). See inner exception |
|||
---> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Failed to generate schema for type - CellularManagement.Domain.Common.OperationResult`1[CellularManagement.Application.Features.NetworkStackConfigs.Commands.CreateNetworkStackConfig.CreateNetworkStackConfigResponse]. See inner exception |
|||
---> System.InvalidOperationException: Can't use schemaId "$StackCoreIMSBindingResponseItem" for type "$CellularManagement.Application.Features.NetworkStackConfigs.Commands.CreateNetworkStackConfig.StackCoreIMSBindingResponseItem". The same schemaId is already used for type "$CellularManagement.Application.Features.NetworkStackConfigs.Queries.GetNetworkStackConfigs.StackCoreIMSBindingResponseItem" |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.RegisterType(Type type, String schemaId) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(DataContract dataContract, SchemaRepository schemaRepository, Func`1 definitionFactory) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.CreateArraySchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass10_0.<GenerateConcreteSchema>b__1() |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForMember(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, DataProperty dataProperty) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.CreateObjectSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass10_0.<GenerateConcreteSchema>b__3() |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(DataContract dataContract, SchemaRepository schemaRepository, Func`1 definitionFactory) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForMember(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, DataProperty dataProperty) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.CreateObjectSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass10_0.<GenerateConcreteSchema>b__3() |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(DataContract dataContract, SchemaRepository schemaRepository, Func`1 definitionFactory) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, PropertyInfo propertyInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
--- End of inner exception stack trace --- |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, PropertyInfo propertyInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.CreateResponseMediaType(ModelMetadata modelMetadata, SchemaRepository schemaRespository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.<>c__DisplayClass25_0.<GenerateResponse>b__2(String contentType) |
|||
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer) |
|||
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateResponse(ApiDescription apiDescription, SchemaRepository schemaRepository, String statusCode, ApiResponseType apiResponseType) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateResponses(ApiDescription apiDescription, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository) |
|||
--- End of inner exception stack trace --- |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerDocumentWithoutFilters(String documentName, String host, String basePath) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerAsync(String documentName, String host, String basePath) |
|||
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) |
|||
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context) |
|||
2025-07-29 00:33:24.410 +08:00 [ERR] DESKTOP-1Q3GI6C [14] An unhandled exception has occurred while executing the request. |
|||
Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Failed to generate Operation for action - CellularManagement.Presentation.Controllers.NetworkStackConfigsController.Update (X1.Presentation). See inner exception |
|||
---> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Failed to generate schema for type - CellularManagement.Application.Features.NetworkStackConfigs.Commands.UpdateNetworkStackConfig.UpdateNetworkStackConfigCommand. See inner exception |
|||
---> System.InvalidOperationException: Can't use schemaId "$StackCoreIMSBindingItem" for type "$CellularManagement.Application.Features.NetworkStackConfigs.Commands.UpdateNetworkStackConfig.StackCoreIMSBindingItem". The same schemaId is already used for type "$CellularManagement.Application.Features.NetworkStackConfigs.Commands.CreateNetworkStackConfig.StackCoreIMSBindingItem" |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.RegisterType(Type type, String schemaId) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(DataContract dataContract, SchemaRepository schemaRepository, Func`1 definitionFactory) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.CreateArraySchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass10_0.<GenerateConcreteSchema>b__1() |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForMember(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, DataProperty dataProperty) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.CreateObjectSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.<>c__DisplayClass10_0.<GenerateConcreteSchema>b__3() |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(DataContract dataContract, SchemaRepository schemaRepository, Func`1 definitionFactory) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForParameter(Type modelType, SchemaRepository schemaRepository, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, PropertyInfo propertyInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
--- End of inner exception stack trace --- |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, PropertyInfo propertyInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateRequestBodyFromBodyParameter(ApiDescription apiDescription, SchemaRepository schemaRepository, ApiParameterDescription bodyParameter) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateRequestBody(ApiDescription apiDescription, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository) |
|||
--- End of inner exception stack trace --- |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerDocumentWithoutFilters(String documentName, String host, String basePath) |
|||
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerAsync(String documentName, String host, String basePath) |
|||
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) |
|||
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context) |
@ -0,0 +1,253 @@ |
|||
import React, { useState, useRef, useEffect } from 'react'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Input } from '@/components/ui/input'; |
|||
import { Textarea } from '@/components/ui/textarea'; |
|||
import { MagnifyingGlassIcon, Cross2Icon } from '@radix-ui/react-icons'; |
|||
|
|||
interface ConfigContentViewerProps { |
|||
content: string; |
|||
className?: string; |
|||
onCopy?: () => void; |
|||
onDownload?: () => void; |
|||
} |
|||
|
|||
export default function ConfigContentViewer({ |
|||
content, |
|||
className = "", |
|||
onCopy, |
|||
onDownload |
|||
}: ConfigContentViewerProps) { |
|||
const [searchTerm, setSearchTerm] = useState(''); |
|||
const [highlightedContent, setHighlightedContent] = useState(''); |
|||
const [isSearchVisible, setIsSearchVisible] = useState(false); |
|||
const [currentMatchIndex, setCurrentMatchIndex] = useState(0); |
|||
const [totalMatches, setTotalMatches] = useState(0); |
|||
|
|||
// 高亮搜索内容
|
|||
useEffect(() => { |
|||
if (!searchTerm.trim()) { |
|||
setHighlightedContent(content); |
|||
setCurrentMatchIndex(0); |
|||
setTotalMatches(0); |
|||
return; |
|||
} |
|||
|
|||
try { |
|||
const escapedSearchTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
|||
const regex = new RegExp(`(${escapedSearchTerm})`, 'gi'); |
|||
const matches = [...content.matchAll(regex)]; |
|||
setTotalMatches(matches.length); |
|||
|
|||
if (matches.length === 0) { |
|||
setHighlightedContent(content); |
|||
return; |
|||
} |
|||
|
|||
// 使用简单的高亮方法
|
|||
let highlighted = content; |
|||
|
|||
// 高亮所有匹配项
|
|||
highlighted = highlighted.replace(regex, (match, group, offset) => { |
|||
// 找到当前匹配项在原始匹配数组中的索引
|
|||
const matchIndex = matches.findIndex(m => m.index === offset); |
|||
const isCurrentMatch = matchIndex === currentMatchIndex; |
|||
|
|||
// 根据是否为当前匹配项选择不同的样式
|
|||
const highlightClass = isCurrentMatch |
|||
? 'bg-blue-500 text-white font-bold' |
|||
: 'bg-yellow-200 text-black'; |
|||
|
|||
return `<span class="${highlightClass}">${match}</span>`; |
|||
}); |
|||
|
|||
setHighlightedContent(highlighted); |
|||
} catch (error) { |
|||
console.error('搜索高亮处理错误:', error); |
|||
setHighlightedContent(content); |
|||
} |
|||
}, [content, searchTerm, currentMatchIndex]); |
|||
|
|||
// 定位到指定匹配项
|
|||
const scrollToMatch = (matchIndex: number) => { |
|||
if (!searchTerm.trim()) return; |
|||
|
|||
const regex = new RegExp(searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'); |
|||
const matches = [...content.matchAll(regex)]; |
|||
|
|||
if (matchIndex >= 0 && matchIndex < matches.length) { |
|||
const match = matches[matchIndex]; |
|||
const startIndex = match.index!; |
|||
|
|||
// 计算匹配项在文本中的位置
|
|||
const textBeforeMatch = content.substring(0, startIndex); |
|||
const linesBeforeMatch = textBeforeMatch.split('\n').length - 1; |
|||
|
|||
// 找到对应的 DOM 元素并滚动到可见位置
|
|||
const container = document.querySelector('.font-mono.text-sm.h-full.overflow-auto'); |
|||
if (container) { |
|||
const lineHeight = parseInt(getComputedStyle(container).lineHeight) || 20; |
|||
const scrollTop = linesBeforeMatch * lineHeight - container.clientHeight / 2; |
|||
container.scrollTop = Math.max(0, scrollTop); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// 搜索下一个匹配项
|
|||
const findNext = () => { |
|||
if (!searchTerm.trim() || totalMatches === 0) return; |
|||
|
|||
const nextIndex = (currentMatchIndex + 1) % totalMatches; |
|||
setCurrentMatchIndex(nextIndex); |
|||
scrollToMatch(nextIndex); |
|||
}; |
|||
|
|||
// 搜索上一个匹配项
|
|||
const findPrevious = () => { |
|||
if (!searchTerm.trim() || totalMatches === 0) return; |
|||
|
|||
const prevIndex = currentMatchIndex === 0 ? totalMatches - 1 : currentMatchIndex - 1; |
|||
setCurrentMatchIndex(prevIndex); |
|||
scrollToMatch(prevIndex); |
|||
}; |
|||
|
|||
// 搜索输入变化时重置匹配索引
|
|||
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
|||
setSearchTerm(e.target.value); |
|||
setCurrentMatchIndex(0); |
|||
}; |
|||
|
|||
// 键盘快捷键支持
|
|||
const handleKeyDown = (e: React.KeyboardEvent) => { |
|||
if (!isSearchVisible || !searchTerm.trim()) return; |
|||
|
|||
if (e.key === 'Enter') { |
|||
e.preventDefault(); |
|||
if (e.shiftKey) { |
|||
findPrevious(); |
|||
} else { |
|||
findNext(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<div className={`flex flex-col h-full ${className}`}> |
|||
{/* 操作按钮区域 */} |
|||
<div className="flex items-center gap-2 mb-4"> |
|||
{/* 搜索功能 */} |
|||
<div className="flex items-center gap-2 flex-1"> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={() => setIsSearchVisible(!isSearchVisible)} |
|||
title="搜索内容" |
|||
className="flex items-center gap-2" |
|||
> |
|||
<MagnifyingGlassIcon className="h-4 w-4" /> |
|||
搜索 |
|||
</Button> |
|||
|
|||
{/* 搜索栏 */} |
|||
{isSearchVisible && ( |
|||
<div className="flex items-center gap-2 flex-1"> |
|||
<div className="relative flex-1"> |
|||
<MagnifyingGlassIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" /> |
|||
<Input |
|||
placeholder="搜索内容... (Enter: 下一个, Shift+Enter: 上一个)" |
|||
value={searchTerm} |
|||
onChange={handleSearchChange} |
|||
onKeyDown={handleKeyDown} |
|||
className="pl-10" |
|||
autoFocus |
|||
/> |
|||
</div> |
|||
{searchTerm && ( |
|||
<span className="text-sm text-gray-500 whitespace-nowrap"> |
|||
{totalMatches > 0 ? ( |
|||
<span className="flex items-center gap-1"> |
|||
<span>{currentMatchIndex + 1} / {totalMatches}</span> |
|||
<span className="w-2 h-2 bg-blue-500 rounded-full"></span> |
|||
</span> |
|||
) : '无匹配项'} |
|||
</span> |
|||
)} |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={findPrevious} |
|||
disabled={!searchTerm.trim() || totalMatches === 0} |
|||
title="上一个 (Shift+Enter)" |
|||
> |
|||
↑ |
|||
</Button> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={findNext} |
|||
disabled={!searchTerm.trim() || totalMatches === 0} |
|||
title="下一个 (Enter)" |
|||
> |
|||
↓ |
|||
</Button> |
|||
<Button |
|||
type="button" |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={() => { |
|||
setSearchTerm(''); |
|||
setIsSearchVisible(false); |
|||
setCurrentMatchIndex(0); |
|||
setTotalMatches(0); |
|||
}} |
|||
title="关闭搜索" |
|||
> |
|||
<Cross2Icon className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
{/* 复制和下载按钮 */} |
|||
<div className="flex items-center gap-2"> |
|||
{onCopy && ( |
|||
<Button |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={onCopy} |
|||
className="flex items-center gap-2" |
|||
> |
|||
复制内容 |
|||
</Button> |
|||
)} |
|||
{onDownload && ( |
|||
<Button |
|||
variant="outline" |
|||
size="sm" |
|||
onClick={onDownload} |
|||
className="flex items-center gap-2" |
|||
> |
|||
下载文件 |
|||
</Button> |
|||
)} |
|||
</div> |
|||
</div> |
|||
|
|||
{/* 配置内容显示区域 */} |
|||
<div className="flex-1 border rounded-md bg-gray-50 overflow-hidden"> |
|||
<div |
|||
className="font-mono text-sm h-full overflow-auto p-3" |
|||
style={{ |
|||
height: '100%', |
|||
whiteSpace: 'pre', |
|||
wordWrap: 'normal', |
|||
overflowWrap: 'normal' |
|||
}} |
|||
dangerouslySetInnerHTML={{ __html: highlightedContent }} |
|||
/> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
@ -0,0 +1,125 @@ |
|||
import React from 'react'; |
|||
import { CoreNetworkConfig } from '@/services/coreNetworkConfigService'; |
|||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { X } from 'lucide-react'; |
|||
import { useToast } from '@/components/ui/use-toast'; |
|||
import ConfigContentViewer from '@/components/ui/ConfigContentViewer'; |
|||
|
|||
interface CoreNetworkConfigViewDialogProps { |
|||
open: boolean; |
|||
onOpenChange: (open: boolean) => void; |
|||
config: CoreNetworkConfig | null; |
|||
} |
|||
|
|||
export default function CoreNetworkConfigViewDialog({ |
|||
open, |
|||
onOpenChange, |
|||
config |
|||
}: CoreNetworkConfigViewDialogProps) { |
|||
const { toast } = useToast(); |
|||
|
|||
// 复制配置内容到剪贴板
|
|||
const handleCopyContent = async () => { |
|||
if (!config?.configContent) return; |
|||
|
|||
try { |
|||
await navigator.clipboard.writeText(config.configContent); |
|||
toast({ |
|||
title: "复制成功", |
|||
description: "配置内容已复制到剪贴板", |
|||
}); |
|||
} catch (error) { |
|||
console.error('复制失败:', error); |
|||
toast({ |
|||
title: "复制失败", |
|||
description: "无法复制到剪贴板,请手动复制", |
|||
variant: "destructive", |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
// 下载配置内容为文件
|
|||
const handleDownloadContent = () => { |
|||
if (!config?.configContent) return; |
|||
|
|||
const blob = new Blob([config.configContent], { type: 'text/plain;charset=utf-8' }); |
|||
const url = URL.createObjectURL(blob); |
|||
const a = document.createElement('a'); |
|||
a.href = url; |
|||
a.download = `${config.name || 'core-network-config'}.txt`; |
|||
document.body.appendChild(a); |
|||
a.click(); |
|||
document.body.removeChild(a); |
|||
URL.revokeObjectURL(url); |
|||
|
|||
toast({ |
|||
title: "下载成功", |
|||
description: "配置文件已下载", |
|||
}); |
|||
}; |
|||
|
|||
if (!config) return null; |
|||
|
|||
return ( |
|||
<Dialog open={open} onOpenChange={onOpenChange}> |
|||
<DialogContent className="max-w-4xl h-[80vh] flex flex-col"> |
|||
<div className="flex items-center justify-between w-full mb-4 flex-shrink-0"> |
|||
<DialogTitle className="text-lg font-semibold"> |
|||
查看配置内容 - {config.name} |
|||
</DialogTitle> |
|||
<Button |
|||
variant="ghost" |
|||
size="sm" |
|||
onClick={() => onOpenChange(false)} |
|||
className="h-8 w-8 p-0" |
|||
> |
|||
<X className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
|
|||
<div className="flex flex-col flex-1 space-y-4 overflow-hidden min-h-0"> |
|||
{/* 配置信息 */} |
|||
<div className="grid grid-cols-2 gap-4 text-sm flex-shrink-0"> |
|||
<div> |
|||
<Label className="text-sm font-medium text-gray-600">配置名称</Label> |
|||
<div className="mt-1 text-gray-900">{config.name}</div> |
|||
</div> |
|||
<div> |
|||
<Label className="text-sm font-medium text-gray-600">状态</Label> |
|||
<div className="mt-1"> |
|||
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${ |
|||
config.isDisabled |
|||
? 'bg-red-100 text-red-800' |
|||
: 'bg-green-100 text-green-800' |
|||
}`}>
|
|||
{config.isDisabled ? '禁用' : '启用'} |
|||
</span> |
|||
</div> |
|||
</div> |
|||
{config.description && ( |
|||
<div className="col-span-2"> |
|||
<Label className="text-sm font-medium text-gray-600">描述</Label> |
|||
<div className="mt-1 text-gray-900">{config.description}</div> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
{/* 配置内容查看器 */} |
|||
<div className="flex flex-col flex-1 min-h-0"> |
|||
<Label className="text-sm font-medium text-gray-600 mb-2 flex-shrink-0"> |
|||
配置内容 |
|||
</Label> |
|||
<ConfigContentViewer |
|||
content={config.configContent || '无配置内容'} |
|||
className="flex-1 min-h-0" |
|||
onCopy={handleCopyContent} |
|||
onDownload={handleDownloadContent} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</DialogContent> |
|||
</Dialog> |
|||
); |
|||
} |
@ -0,0 +1,125 @@ |
|||
import React from 'react'; |
|||
import { IMSConfiguration } from '@/services/imsConfigurationService'; |
|||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { X } from 'lucide-react'; |
|||
import { useToast } from '@/components/ui/use-toast'; |
|||
import ConfigContentViewer from '@/components/ui/ConfigContentViewer'; |
|||
|
|||
interface IMSConfigurationViewDialogProps { |
|||
open: boolean; |
|||
onOpenChange: (open: boolean) => void; |
|||
configuration: IMSConfiguration | null; |
|||
} |
|||
|
|||
export default function IMSConfigurationViewDialog({ |
|||
open, |
|||
onOpenChange, |
|||
configuration |
|||
}: IMSConfigurationViewDialogProps) { |
|||
const { toast } = useToast(); |
|||
|
|||
// 复制配置内容到剪贴板
|
|||
const handleCopyContent = async () => { |
|||
if (!configuration?.configContent) return; |
|||
|
|||
try { |
|||
await navigator.clipboard.writeText(configuration.configContent); |
|||
toast({ |
|||
title: "复制成功", |
|||
description: "配置内容已复制到剪贴板", |
|||
}); |
|||
} catch (error) { |
|||
console.error('复制失败:', error); |
|||
toast({ |
|||
title: "复制失败", |
|||
description: "无法复制到剪贴板,请手动复制", |
|||
variant: "destructive", |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
// 下载配置内容为文件
|
|||
const handleDownloadContent = () => { |
|||
if (!configuration?.configContent) return; |
|||
|
|||
const blob = new Blob([configuration.configContent], { type: 'text/plain;charset=utf-8' }); |
|||
const url = URL.createObjectURL(blob); |
|||
const a = document.createElement('a'); |
|||
a.href = url; |
|||
a.download = `${configuration.name || 'ims-config'}.txt`; |
|||
document.body.appendChild(a); |
|||
a.click(); |
|||
document.body.removeChild(a); |
|||
URL.revokeObjectURL(url); |
|||
|
|||
toast({ |
|||
title: "下载成功", |
|||
description: "配置文件已下载", |
|||
}); |
|||
}; |
|||
|
|||
if (!configuration) return null; |
|||
|
|||
return ( |
|||
<Dialog open={open} onOpenChange={onOpenChange}> |
|||
<DialogContent className="max-w-4xl h-[80vh] flex flex-col"> |
|||
<div className="flex items-center justify-between w-full mb-4 flex-shrink-0"> |
|||
<DialogTitle className="text-lg font-semibold"> |
|||
查看配置内容 - {configuration.name} |
|||
</DialogTitle> |
|||
<Button |
|||
variant="ghost" |
|||
size="sm" |
|||
onClick={() => onOpenChange(false)} |
|||
className="h-8 w-8 p-0" |
|||
> |
|||
<X className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
|
|||
<div className="flex flex-col flex-1 space-y-4 overflow-hidden min-h-0"> |
|||
{/* 配置信息 */} |
|||
<div className="grid grid-cols-2 gap-4 text-sm flex-shrink-0"> |
|||
<div> |
|||
<Label className="text-sm font-medium text-gray-600">配置名称</Label> |
|||
<div className="mt-1 text-gray-900">{configuration.name}</div> |
|||
</div> |
|||
<div> |
|||
<Label className="text-sm font-medium text-gray-600">状态</Label> |
|||
<div className="mt-1"> |
|||
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${ |
|||
configuration.isDisabled |
|||
? 'bg-red-100 text-red-800' |
|||
: 'bg-green-100 text-green-800' |
|||
}`}>
|
|||
{configuration.isDisabled ? '禁用' : '启用'} |
|||
</span> |
|||
</div> |
|||
</div> |
|||
{configuration.description && ( |
|||
<div className="col-span-2"> |
|||
<Label className="text-sm font-medium text-gray-600">描述</Label> |
|||
<div className="mt-1 text-gray-900">{configuration.description}</div> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
{/* 配置内容查看器 */} |
|||
<div className="flex flex-col flex-1 min-h-0"> |
|||
<Label className="text-sm font-medium text-gray-600 mb-2 flex-shrink-0"> |
|||
配置内容 |
|||
</Label> |
|||
<ConfigContentViewer |
|||
content={configuration.configContent || '无配置内容'} |
|||
className="flex-1 min-h-0" |
|||
onCopy={handleCopyContent} |
|||
onDownload={handleDownloadContent} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</DialogContent> |
|||
</Dialog> |
|||
); |
|||
} |
@ -0,0 +1,125 @@ |
|||
import React from 'react'; |
|||
import { RANConfiguration } from '@/services/ranConfigurationService'; |
|||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'; |
|||
import { Button } from '@/components/ui/button'; |
|||
import { Label } from '@/components/ui/label'; |
|||
import { X } from 'lucide-react'; |
|||
import { useToast } from '@/components/ui/use-toast'; |
|||
import ConfigContentViewer from '@/components/ui/ConfigContentViewer'; |
|||
|
|||
interface RANConfigurationViewDialogProps { |
|||
open: boolean; |
|||
onOpenChange: (open: boolean) => void; |
|||
configuration: RANConfiguration | null; |
|||
} |
|||
|
|||
export default function RANConfigurationViewDialog({ |
|||
open, |
|||
onOpenChange, |
|||
configuration |
|||
}: RANConfigurationViewDialogProps) { |
|||
const { toast } = useToast(); |
|||
|
|||
// 复制配置内容到剪贴板
|
|||
const handleCopyContent = async () => { |
|||
if (!configuration?.configContent) return; |
|||
|
|||
try { |
|||
await navigator.clipboard.writeText(configuration.configContent); |
|||
toast({ |
|||
title: "复制成功", |
|||
description: "配置内容已复制到剪贴板", |
|||
}); |
|||
} catch (error) { |
|||
console.error('复制失败:', error); |
|||
toast({ |
|||
title: "复制失败", |
|||
description: "无法复制到剪贴板,请手动复制", |
|||
variant: "destructive", |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
// 下载配置内容为文件
|
|||
const handleDownloadContent = () => { |
|||
if (!configuration?.configContent) return; |
|||
|
|||
const blob = new Blob([configuration.configContent], { type: 'text/plain;charset=utf-8' }); |
|||
const url = URL.createObjectURL(blob); |
|||
const a = document.createElement('a'); |
|||
a.href = url; |
|||
a.download = `${configuration.name || 'ran-config'}.txt`; |
|||
document.body.appendChild(a); |
|||
a.click(); |
|||
document.body.removeChild(a); |
|||
URL.revokeObjectURL(url); |
|||
|
|||
toast({ |
|||
title: "下载成功", |
|||
description: "配置文件已下载", |
|||
}); |
|||
}; |
|||
|
|||
if (!configuration) return null; |
|||
|
|||
return ( |
|||
<Dialog open={open} onOpenChange={onOpenChange}> |
|||
<DialogContent className="max-w-4xl h-[80vh] flex flex-col"> |
|||
<div className="flex items-center justify-between w-full mb-4 flex-shrink-0"> |
|||
<DialogTitle className="text-lg font-semibold"> |
|||
查看配置内容 - {configuration.name} |
|||
</DialogTitle> |
|||
<Button |
|||
variant="ghost" |
|||
size="sm" |
|||
onClick={() => onOpenChange(false)} |
|||
className="h-8 w-8 p-0" |
|||
> |
|||
<X className="h-4 w-4" /> |
|||
</Button> |
|||
</div> |
|||
|
|||
<div className="flex flex-col flex-1 space-y-4 overflow-hidden min-h-0"> |
|||
{/* 配置信息 */} |
|||
<div className="grid grid-cols-2 gap-4 text-sm flex-shrink-0"> |
|||
<div> |
|||
<Label className="text-sm font-medium text-gray-600">配置名称</Label> |
|||
<div className="mt-1 text-gray-900">{configuration.name}</div> |
|||
</div> |
|||
<div> |
|||
<Label className="text-sm font-medium text-gray-600">状态</Label> |
|||
<div className="mt-1"> |
|||
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${ |
|||
configuration.isDisabled |
|||
? 'bg-red-100 text-red-800' |
|||
: 'bg-green-100 text-green-800' |
|||
}`}>
|
|||
{configuration.isDisabled ? '禁用' : '启用'} |
|||
</span> |
|||
</div> |
|||
</div> |
|||
{configuration.description && ( |
|||
<div className="col-span-2"> |
|||
<Label className="text-sm font-medium text-gray-600">描述</Label> |
|||
<div className="mt-1 text-gray-900">{configuration.description}</div> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
{/* 配置内容查看器 */} |
|||
<div className="flex flex-col flex-1 min-h-0"> |
|||
<Label className="text-sm font-medium text-gray-600 mb-2 flex-shrink-0"> |
|||
配置内容 |
|||
</Label> |
|||
<ConfigContentViewer |
|||
content={configuration.configContent || '无配置内容'} |
|||
className="flex-1 min-h-0" |
|||
onCopy={handleCopyContent} |
|||
onDownload={handleDownloadContent} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</DialogContent> |
|||
</Dialog> |
|||
); |
|||
} |
Loading…
Reference in new issue