From 5d0566e397ab046b0bbc33b44f7edff657b8f87b Mon Sep 17 00:00:00 2001 From: root Date: Wed, 20 Aug 2025 10:12:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8DADB=E5=92=8CAT?= =?UTF-8?q?=E6=93=8D=E4=BD=9CCommandHandler=E4=B8=AD=E7=BC=BA=E5=B0=91IUni?= =?UTF-8?q?tOfWork=20SaveChangesAsync=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为CreateAdbOperationCommandHandler添加IUnitOfWork依赖和SaveChangesAsync调用 - 为UpdateAdbOperationCommandHandler添加IUnitOfWork依赖和SaveChangesAsync调用 - 为DeleteAdbOperationCommandHandler添加IUnitOfWork依赖和SaveChangesAsync调用 - 为CreateAtOperationCommandHandler添加IUnitOfWork依赖和SaveChangesAsync调用 - 为UpdateAtOperationCommandHandler添加IUnitOfWork依赖和SaveChangesAsync调用 - 为DeleteAtOperationCommandHandler添加IUnitOfWork依赖和SaveChangesAsync调用 修复DDD设计原则违反问题,确保数据被正确持久化到数据库,支持事务管理和异常处理。 --- .../CreateAdbOperationCommandHandler.cs | 9 +- .../DeleteAdbOperationCommandHandler.cs | 9 +- .../UpdateAdbOperationCommand.cs | 5 - .../UpdateAdbOperationCommandHandler.cs | 21 +- .../CreateAtOperationCommandHandler.cs | 9 +- .../DeleteAtOperationCommandHandler.cs | 9 +- .../UpdateAtOperationCommandHandler.cs | 13 +- .../Entities/Terminal/AdbOperation.cs | 16 +- .../Terminal/AdbOperationConfiguration.cs | 4 +- ...UpdateAdbOperationPathNullable.Designer.cs | 1625 +++++++++++++++++ ...20020118_UpdateAdbOperationPathNullable.cs | 44 + .../Migrations/AppDbContextModelSnapshot.cs | 3 +- src/X1.WebAPI/Properties/launchSettings.json | 4 +- src/X1.WebUI/package-lock.json | 469 +++++ src/X1.WebUI/src/constants/api.ts | 3 + .../adb-operations/AdbOperationDrawer.tsx | 442 +++++ .../pages/adb-operations/AdbOperationForm.tsx | 355 ---- .../adb-operations/AdbOperationsView.tsx | 72 +- .../src/services/adbOperationsService.ts | 66 +- src/X1.WebUI/yarn.lock | 392 +--- src/modify.md | 1493 +++++++-------- 21 files changed, 3511 insertions(+), 1552 deletions(-) create mode 100644 src/X1.Infrastructure/Migrations/20250820020118_UpdateAdbOperationPathNullable.Designer.cs create mode 100644 src/X1.Infrastructure/Migrations/20250820020118_UpdateAdbOperationPathNullable.cs create mode 100644 src/X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx delete mode 100644 src/X1.WebUI/src/pages/adb-operations/AdbOperationForm.tsx diff --git a/src/X1.Application/Features/AdbOperations/Commands/CreateAdbOperation/CreateAdbOperationCommandHandler.cs b/src/X1.Application/Features/AdbOperations/Commands/CreateAdbOperation/CreateAdbOperationCommandHandler.cs index 32dda0f..85e8565 100644 --- a/src/X1.Application/Features/AdbOperations/Commands/CreateAdbOperation/CreateAdbOperationCommandHandler.cs +++ b/src/X1.Application/Features/AdbOperations/Commands/CreateAdbOperation/CreateAdbOperationCommandHandler.cs @@ -2,6 +2,7 @@ using CellularManagement.Domain.Common; using CellularManagement.Domain.Entities.Terminal; using CellularManagement.Domain.Repositories.Terminal; using CellularManagement.Domain.Services; +using CellularManagement.Domain.Repositories.Base; using MediatR; using Microsoft.Extensions.Logging; @@ -15,15 +16,18 @@ public class CreateAdbOperationCommandHandler : IRequestHandler _logger; private readonly ICurrentUserService _currentUserService; + private readonly IUnitOfWork _unitOfWork; public CreateAdbOperationCommandHandler( IAdbOperationRepository adbOperationRepository, ILogger logger, - ICurrentUserService currentUserService) + ICurrentUserService currentUserService, + IUnitOfWork unitOfWork) { _adbOperationRepository = adbOperationRepository; _logger = logger; _currentUserService = currentUserService; + _unitOfWork = unitOfWork; } public async Task> Handle( @@ -52,6 +56,9 @@ public class CreateAdbOperationCommandHandler : IRequestHandler _logger; + private readonly IUnitOfWork _unitOfWork; public DeleteAdbOperationCommandHandler( IAdbOperationRepository adbOperationRepository, - ILogger logger) + ILogger logger, + IUnitOfWork unitOfWork) { _adbOperationRepository = adbOperationRepository; _logger = logger; + _unitOfWork = unitOfWork; } public async Task> Handle( @@ -38,6 +42,9 @@ public class DeleteAdbOperationCommandHandler : IRequestHandler public string Id { get; set; } = string.Empty; - /// - /// 设备ID - /// - public string DeviceId { get; set; } = string.Empty; - /// /// 执行的ADB命令 /// diff --git a/src/X1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommandHandler.cs b/src/X1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommandHandler.cs index 13b78f4..72d9799 100644 --- a/src/X1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommandHandler.cs +++ b/src/X1.Application/Features/AdbOperations/Commands/UpdateAdbOperation/UpdateAdbOperationCommandHandler.cs @@ -1,5 +1,6 @@ using CellularManagement.Domain.Repositories.Terminal; using CellularManagement.Domain.Services; +using CellularManagement.Domain.Repositories.Base; using MediatR; using Microsoft.Extensions.Logging; using CellularManagement.Domain.Common; @@ -14,23 +15,26 @@ public class UpdateAdbOperationCommandHandler : IRequestHandler _logger; private readonly ICurrentUserService _currentUserService; + private readonly IUnitOfWork _unitOfWork; public UpdateAdbOperationCommandHandler( IAdbOperationRepository adbOperationRepository, ILogger logger, - ICurrentUserService currentUserService) + ICurrentUserService currentUserService, + IUnitOfWork unitOfWork) { _adbOperationRepository = adbOperationRepository; _logger = logger; _currentUserService = currentUserService; + _unitOfWork = unitOfWork; } public async Task> Handle( UpdateAdbOperationCommand request, CancellationToken cancellationToken) { - _logger.LogInformation("开始更新ADB操作,ID: {Id}, 设备ID: {DeviceId}, 命令: {Command}, 路径: {Path}", - request.Id, request.DeviceId, request.Command, request.Path); + _logger.LogInformation("开始更新ADB操作,ID: {Id}, 命令: {Command}, 路径: {Path}", + request.Id, request.Command, request.Path); try { @@ -45,9 +49,9 @@ public class UpdateAdbOperationCommandHandler : IRequestHandler.CreateFailure("更新ADB操作时发生错误"); } } diff --git a/src/X1.Application/Features/AtOperations/Commands/CreateAtOperation/CreateAtOperationCommandHandler.cs b/src/X1.Application/Features/AtOperations/Commands/CreateAtOperation/CreateAtOperationCommandHandler.cs index c9e7e03..f6701ae 100644 --- a/src/X1.Application/Features/AtOperations/Commands/CreateAtOperation/CreateAtOperationCommandHandler.cs +++ b/src/X1.Application/Features/AtOperations/Commands/CreateAtOperation/CreateAtOperationCommandHandler.cs @@ -2,6 +2,7 @@ using CellularManagement.Domain.Common; using CellularManagement.Domain.Entities.Terminal; using CellularManagement.Domain.Repositories.Terminal; using CellularManagement.Domain.Services; +using CellularManagement.Domain.Repositories.Base; using MediatR; using Microsoft.Extensions.Logging; @@ -15,15 +16,18 @@ public class CreateAtOperationCommandHandler : IRequestHandler _logger; private readonly ICurrentUserService _currentUserService; + private readonly IUnitOfWork _unitOfWork; public CreateAtOperationCommandHandler( IAtOperationRepository atOperationRepository, ILogger logger, - ICurrentUserService currentUserService) + ICurrentUserService currentUserService, + IUnitOfWork unitOfWork) { _atOperationRepository = atOperationRepository; _logger = logger; _currentUserService = currentUserService; + _unitOfWork = unitOfWork; } public async Task> Handle( @@ -55,6 +59,9 @@ public class CreateAtOperationCommandHandler : IRequestHandler _logger; + private readonly IUnitOfWork _unitOfWork; public DeleteAtOperationCommandHandler( IAtOperationRepository atOperationRepository, - ILogger logger) + ILogger logger, + IUnitOfWork unitOfWork) { _atOperationRepository = atOperationRepository; _logger = logger; + _unitOfWork = unitOfWork; } public async Task> Handle( @@ -39,6 +43,9 @@ public class DeleteAtOperationCommandHandler : IRequestHandler _logger; private readonly ICurrentUserService _currentUserService; + private readonly IUnitOfWork _unitOfWork; public UpdateAtOperationCommandHandler( IAtOperationRepository atOperationRepository, ILogger logger, - ICurrentUserService currentUserService) + ICurrentUserService currentUserService, + IUnitOfWork unitOfWork) { _atOperationRepository = atOperationRepository; _logger = logger; _currentUserService = currentUserService; + _unitOfWork = unitOfWork; } public async Task> Handle( @@ -62,6 +66,9 @@ public class UpdateAtOperationCommandHandler : IRequestHandler.CreateFailure("更新AT操作时发生错误"); } } diff --git a/src/X1.Domain/Entities/Terminal/AdbOperation.cs b/src/X1.Domain/Entities/Terminal/AdbOperation.cs index 5c469d4..eb52fb4 100644 --- a/src/X1.Domain/Entities/Terminal/AdbOperation.cs +++ b/src/X1.Domain/Entities/Terminal/AdbOperation.cs @@ -85,8 +85,9 @@ public class AdbOperation : AuditableEntity if (string.IsNullOrWhiteSpace(description)) throw new ArgumentException("操作描述不能为空", nameof(description)); - if (string.IsNullOrWhiteSpace(path)) - throw new ArgumentException("路径不能为空", nameof(path)); + // 当启用绝对路径时,路径不能为空 + if (useAbsolutePath && string.IsNullOrWhiteSpace(path)) + throw new ArgumentException("启用绝对路径时,路径不能为空", nameof(path)); if (string.IsNullOrWhiteSpace(createdBy)) throw new ArgumentException("创建人不能为空", nameof(createdBy)); @@ -138,14 +139,19 @@ public class AdbOperation : AuditableEntity if (string.IsNullOrWhiteSpace(deviceId)) throw new ArgumentException("设备ID不能为空", nameof(deviceId)); + // 设备ID不允许修改 + if (!string.Equals(DeviceId, deviceId.Trim(), StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException("设备ID不允许修改", nameof(deviceId)); + if (string.IsNullOrWhiteSpace(command)) throw new ArgumentException("ADB命令不能为空", nameof(command)); if (string.IsNullOrWhiteSpace(description)) throw new ArgumentException("操作描述不能为空", nameof(description)); - if (string.IsNullOrWhiteSpace(path)) - throw new ArgumentException("路径不能为空", nameof(path)); + // 当启用绝对路径时,路径不能为空 + if (useAbsolutePath && string.IsNullOrWhiteSpace(path)) + throw new ArgumentException("启用绝对路径时,路径不能为空", nameof(path)); if (string.IsNullOrWhiteSpace(updatedBy)) throw new ArgumentException("更新人不能为空", nameof(updatedBy)); @@ -153,7 +159,7 @@ public class AdbOperation : AuditableEntity if (waitTimeMs < 0) throw new ArgumentException("等待时间不能为负数", nameof(waitTimeMs)); - DeviceId = deviceId.Trim(); + // 不更新DeviceId,保持原有值 Command = command.Trim(); Description = description.Trim(); Path = path.Trim(); diff --git a/src/X1.Infrastructure/Configurations/Terminal/AdbOperationConfiguration.cs b/src/X1.Infrastructure/Configurations/Terminal/AdbOperationConfiguration.cs index f789550..9e46c15 100644 --- a/src/X1.Infrastructure/Configurations/Terminal/AdbOperationConfiguration.cs +++ b/src/X1.Infrastructure/Configurations/Terminal/AdbOperationConfiguration.cs @@ -41,9 +41,9 @@ public class AdbOperationConfiguration : IEntityTypeConfiguration // 路径字段 builder.Property(x => x.Path) - .IsRequired() + .IsRequired(false) .HasMaxLength(500) - .HasComment("命令执行时所依赖的路径"); + .HasComment("命令执行时所依赖的路径(当启用绝对路径时必填)"); // 绝对路径字段 builder.Property(x => x.UseAbsolutePath) diff --git a/src/X1.Infrastructure/Migrations/20250820020118_UpdateAdbOperationPathNullable.Designer.cs b/src/X1.Infrastructure/Migrations/20250820020118_UpdateAdbOperationPathNullable.Designer.cs new file mode 100644 index 0000000..7032083 --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250820020118_UpdateAdbOperationPathNullable.Designer.cs @@ -0,0 +1,1625 @@ +// +using System; +using CellularManagement.Infrastructure.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250820020118_UpdateAdbOperationPathNullable")] + partial class UpdateAdbOperationPathNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("CellularManagement.Domain.Entities.AppRole", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("角色ID,主键"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text") + .HasComment("并发控制戳"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("角色描述"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("角色名称"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("标准化角色名称(大写)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_Roles_Name"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("tb_roles", null, t => + { + t.HasComment("角色表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.AppUser", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("用户ID,主键"); + + b.Property("AccessFailedCount") + .HasColumnType("integer") + .HasComment("登录失败次数"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text") + .HasComment("并发控制戳"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("电子邮箱"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean") + .HasComment("邮箱是否已验证"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("用户状态(true: 启用, false: 禁用)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否已删除"); + + b.Property("LastLoginTime") + .HasColumnType("timestamp with time zone") + .HasComment("最后登录时间"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean") + .HasComment("是否启用账户锁定"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone") + .HasComment("账户锁定结束时间"); + + b.Property("ModifiedTime") + .HasColumnType("timestamp with time zone") + .HasComment("修改时间"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("标准化电子邮箱(大写)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("标准化账号(大写)"); + + b.Property("PasswordHash") + .HasColumnType("text") + .HasComment("密码哈希值"); + + b.Property("PhoneNumber") + .IsRequired() + .HasColumnType("text") + .HasComment("电话号码"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean") + .HasComment("电话号码是否已验证"); + + b.Property("RealName") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("用户名"); + + b.Property("SecurityStamp") + .HasColumnType("text") + .HasComment("安全戳,用于并发控制"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean") + .HasComment("是否启用双因素认证"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("账号"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasDatabaseName("IX_user_Email"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasDatabaseName("IX_user_PhoneNumber"); + + b.HasIndex("UserName") + .IsUnique() + .HasDatabaseName("IX_user_UserName"); + + b.ToTable("tb_users", null, t => + { + t.HasComment("用户表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDevice", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("设备ID"); + + b.Property("AgentPort") + .HasColumnType("integer") + .HasComment("Agent端口"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("设备描述"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("设备编码"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("character varying(45)") + .HasComment("IP地址"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("设备名称"); + + b.Property("SerialNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("序列号"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique() + .HasDatabaseName("IX_cellular_device_DeviceCode"); + + b.HasIndex("SerialNumber") + .IsUnique() + .HasDatabaseName("IX_cellular_device_SerialNumber"); + + b.ToTable("tb_cellular_device", null, t => + { + t.HasComment("蜂窝设备表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDeviceRuntime", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("NetworkStackCode") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RuntimeCode") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RuntimeStatus") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("IsDeleted"); + + b.HasIndex("NetworkStackCode"); + + b.HasIndex("RuntimeCode"); + + b.HasIndex("RuntimeStatus"); + + b.HasIndex("DeviceCode", "CreatedAt") + .HasDatabaseName("IX_CellularDeviceRuntimes_DeviceCode_CreatedAt"); + + b.ToTable("tb_cellular_device_runtimes", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDeviceRuntimeDetail", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("NetworkStackCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RuntimeCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RuntimeStatus") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_CellularDeviceRuntimeDetails_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_CellularDeviceRuntimeDetails_CreatedBy"); + + b.HasIndex("RuntimeCode") + .HasDatabaseName("IX_CellularDeviceRuntimeDetails_RuntimeCode"); + + b.HasIndex("RuntimeStatus") + .HasDatabaseName("IX_CellularDeviceRuntimeDetails_RuntimeStatus"); + + b.ToTable("tb_cellular_device_runtime_details", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.ProtocolVersion", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("版本ID"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("版本描述"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用"); + + b.Property("MinimumSupportedVersion") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasComment("最低支持版本"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("版本名称"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp with time zone") + .HasComment("发布日期"); + + b.Property("SerialNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("设备序列号"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasComment("版本号"); + + b.HasKey("Id"); + + b.HasIndex("SerialNumber") + .HasDatabaseName("IX_ProtocolVersions_SerialNumber"); + + b.HasIndex("Version") + .HasDatabaseName("IX_ProtocolVersions_Version"); + + b.ToTable("tb_protocol_versions", null, t => + { + t.HasComment("协议版本表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.LoginLog", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("日志ID"); + + b.Property("Browser") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("浏览器信息"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FailureReason") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasComment("失败原因"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("登录IP"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsSuccess") + .HasColumnType("boolean") + .HasComment("登录状态(成功/失败)"); + + b.Property("Location") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasComment("登录位置"); + + b.Property("LoginSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("LoginTime") + .HasColumnType("timestamp with time zone") + .HasComment("登录时间"); + + b.Property("LoginType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OperatingSystem") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("操作系统信息"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserAgent") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("设备信息"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasComment("用户ID"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress") + .HasDatabaseName("IX_LoginLogs_IpAddress"); + + b.HasIndex("LoginTime") + .HasDatabaseName("IX_LoginLogs_LoginTime"); + + b.HasIndex("UserId") + .HasDatabaseName("IX_LoginLogs_UserId"); + + b.HasIndex("UserId", "LoginTime") + .HasDatabaseName("IX_LoginLogs_UserId_LoginTime"); + + b.ToTable("tb_login_logs", null, t => + { + t.HasComment("用户登录日志表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.ProtocolLog", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("主键ID"); + + b.Property("CellID") + .HasColumnType("integer") + .HasComment("小区ID"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("设备代码"); + + b.Property("Direction") + .HasColumnType("integer") + .HasComment("日志方向类型"); + + b.Property("IMSI") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("国际移动用户识别码"); + + b.Property("Info") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("信息字段"); + + b.Property("LayerType") + .HasMaxLength(50) + .HasColumnType("integer") + .HasComment("协议层类型"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasComment("消息字段"); + + b.Property("MessageDetailJson") + .HasColumnType("text") + .HasComment("消息详情集合(JSON格式存储)"); + + b.Property("MessageId") + .HasColumnType("bigint") + .HasComment("消息ID"); + + b.Property("PLMN") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasComment("公共陆地移动网络标识"); + + b.Property("RuntimeCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("运行时代码"); + + b.Property("TimeMs") + .HasColumnType("bigint") + .HasComment("时间间隔(毫秒)"); + + b.Property("Timestamp") + .HasColumnType("bigint") + .HasComment("时间戳"); + + b.Property("UEID") + .HasColumnType("integer") + .HasComment("用户设备ID"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .HasDatabaseName("IX_ProtocolLog_DeviceCode"); + + b.HasIndex("LayerType") + .HasDatabaseName("IX_ProtocolLog_LayerType"); + + b.HasIndex("MessageId") + .HasDatabaseName("IX_ProtocolLog_MessageId"); + + b.HasIndex("RuntimeCode") + .HasDatabaseName("IX_ProtocolLog_RuntimeCode"); + + b.HasIndex("Timestamp") + .HasDatabaseName("IX_ProtocolLog_Timestamp"); + + b.HasIndex("DeviceCode", "RuntimeCode") + .HasDatabaseName("IX_ProtocolLog_DeviceCode_RuntimeCode"); + + b.HasIndex("DeviceCode", "Timestamp") + .HasDatabaseName("IX_ProtocolLog_DeviceCode_Timestamp"); + + b.ToTable("tb_protocol_logs", null, t => + { + t.HasComment("协议日志表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.CoreNetworkConfig", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("配置ID"); + + b.Property("ConfigContent") + .IsRequired() + .HasColumnType("text") + .HasComment("配置内容(JSON格式)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("配置描述"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDisabled") + .HasColumnType("boolean") + .HasComment("是否禁用"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("配置名称"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_CoreNetworkConfigs_Name"); + + b.ToTable("tb_core_network_configs", null, t => + { + t.HasComment("核心网配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.IMS_Configuration", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("配置ID"); + + b.Property("ConfigContent") + .IsRequired() + .HasColumnType("text") + .HasComment("配置内容(JSON格式)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("配置描述"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDisabled") + .HasColumnType("boolean") + .HasComment("是否禁用"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("配置名称"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_IMS_Configurations_Name"); + + b.ToTable("tb_ims_configurations", null, t => + { + t.HasComment("IMS配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("配置ID"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("描述"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasComment("是否激活"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetworkStackCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("网络栈编码"); + + b.Property("NetworkStackName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("网络栈名称"); + + b.Property("RanId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("RAN配置ID"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_NetworkStackConfigs_IsActive"); + + b.HasIndex("NetworkStackCode") + .IsUnique() + .HasDatabaseName("IX_NetworkStackConfigs_NetworkStackCode"); + + b.HasIndex("NetworkStackName") + .IsUnique() + .HasDatabaseName("IX_NetworkStackConfigs_NetworkStackName"); + + b.HasIndex("RanId") + .HasDatabaseName("IX_NetworkStackConfigs_RanId"); + + b.ToTable("tb_network_stack_configs", null, t => + { + t.HasComment("网络栈配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.RAN_Configuration", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("配置ID"); + + b.Property("ConfigContent") + .IsRequired() + .HasColumnType("text") + .HasComment("配置内容(JSON格式)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("配置描述"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDisabled") + .HasColumnType("boolean") + .HasComment("是否禁用"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("配置名称"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_RAN_Configurations_Name"); + + b.ToTable("tb_ran_configurations", null, t => + { + t.HasComment("RAN配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.Stack_CoreIMS_Binding", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("绑定关系ID"); + + b.Property("CnId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("核心网配置ID"); + + b.Property("ImsId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("IMS配置ID"); + + b.Property("Index") + .HasColumnType("integer") + .HasComment("索引"); + + b.Property("NetworkStackConfigId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("网络栈配置ID"); + + b.HasKey("Id"); + + b.HasIndex("CnId") + .HasDatabaseName("IX_Stack_CoreIMS_Bindings_CnId"); + + b.HasIndex("ImsId") + .HasDatabaseName("IX_Stack_CoreIMS_Bindings_ImsId"); + + b.HasIndex("NetworkStackConfigId") + .HasDatabaseName("IX_Stack_CoreIMS_Bindings_NetworkStackConfigId"); + + b.HasIndex("NetworkStackConfigId", "Index") + .IsUnique() + .HasDatabaseName("IX_Stack_CoreIMS_Bindings_NetworkStackConfigId_Index"); + + b.ToTable("tb_stack_core_ims_bindings", null, t => + { + t.HasComment("栈与核心网/IMS绑定关系表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.ToTable("tb_permissions", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("text"); + + b.Property("PermissionId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("tb_role_permissions", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Terminal.AdbOperation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Command") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasComment("执行的ADB命令"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("创建人"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("ADB操作的描述"); + + b.Property("DeviceId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("设备ID"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否启用"); + + b.Property("Path") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("命令执行时所依赖的路径(当启用绝对路径时必填)"); + + b.Property("ScreenshotData") + .HasColumnType("BYTEA") + .HasComment("操作截图数据(字节数组)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("更新人"); + + b.Property("UseAbsolutePath") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否启用绝对路径"); + + b.Property("WaitTimeMs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("执行命令完需要等待的时间(毫秒)"); + + b.HasKey("Id"); + + b.HasIndex("Command") + .HasDatabaseName("IX_AdbOperations_Command"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_AdbOperations_CreatedAt"); + + b.HasIndex("DeviceId") + .HasDatabaseName("IX_AdbOperations_DeviceId"); + + b.HasIndex("IsEnabled") + .HasDatabaseName("IX_AdbOperations_IsEnabled"); + + b.ToTable("tb_adboperations", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Terminal.AtOperation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BaudRate") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(115200) + .HasComment("波特率"); + + b.Property("Command") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasComment("AT命令内容"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("创建人"); + + b.Property("DataBits") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(8) + .HasComment("数据位"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("操作描述"); + + b.Property("DeviceId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("设备ID"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否启用"); + + b.Property("Parameters") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasDefaultValue("") + .HasComment("命令参数(JSON格式)"); + + b.Property("Parity") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasDefaultValue("NONE") + .HasComment("校验位"); + + b.Property("Port") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("串口端口"); + + b.Property("ScreenshotData") + .HasColumnType("BYTEA") + .HasComment("操作截图数据(字节数组)"); + + b.Property("StopBits") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasDefaultValue("1") + .HasComment("停止位"); + + b.Property("Timeout") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(30) + .HasComment("超时时间(秒)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("更新人"); + + b.HasKey("Id"); + + b.HasIndex("Command") + .HasDatabaseName("IX_AtOperations_Command"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_AtOperations_CreatedAt"); + + b.HasIndex("DeviceId") + .HasDatabaseName("IX_AtOperations_DeviceId"); + + b.HasIndex("IsEnabled") + .HasDatabaseName("IX_AtOperations_IsEnabled"); + + b.ToTable("tb_atoperations", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Terminal.TerminalDevice", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("Alias") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AndroidVersion") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("BootSerial") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Brand") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BuildId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BuildType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Device") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Hardware") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("HardwarePlatform") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LastConnectedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastDisconnectedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Locale") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Model") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SdkVersion") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Serial") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ServiceSerial") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AndroidVersion"); + + b.HasIndex("Brand"); + + b.HasIndex("Hardware"); + + b.HasIndex("IsEnabled"); + + b.HasIndex("LastConnectedAt"); + + b.HasIndex("Model"); + + b.HasIndex("Serial") + .IsUnique(); + + b.HasIndex("ServiceSerial"); + + b.HasIndex("Status"); + + b.ToTable("tb_terminal_devices", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Terminal.TerminalService", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("AgentPort") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("character varying(45)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsServiceStarted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SerialNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ServiceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ServiceType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IpAddress"); + + b.HasIndex("IsEnabled"); + + b.HasIndex("IsServiceStarted"); + + b.HasIndex("SerialNumber") + .IsUnique(); + + b.HasIndex("ServiceCode") + .IsUnique(); + + b.HasIndex("ServiceType"); + + b.ToTable("tb_terminal_services", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.TestCase.CaseStepConfig", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("createdat"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("createdby"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("description"); + + b.Property("Icon") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("icon"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasColumnName("isenabled"); + + b.Property("Mapping") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mapping"); + + b.Property("StepName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("stepname"); + + b.Property("StepType") + .HasColumnType("integer") + .HasColumnName("steptype"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updatedat"); + + b.Property("UpdatedBy") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("updatedby"); + + b.HasKey("Id"); + + b.HasIndex("IsEnabled") + .HasDatabaseName("ix_tb_casestepconfig_isenabled"); + + b.HasIndex("StepName") + .HasDatabaseName("ix_tb_casestepconfig_stepname"); + + b.HasIndex("StepType") + .HasDatabaseName("ix_tb_casestepconfig_steptype"); + + b.ToTable("tb_casestepconfig", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("tb_user_roles", null, t => + { + t.HasComment("用户角色关系表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDeviceRuntime", b => + { + b.HasOne("CellularManagement.Domain.Entities.Device.CellularDevice", "Device") + .WithOne("Runtime") + .HasForeignKey("CellularManagement.Domain.Entities.Device.CellularDeviceRuntime", "DeviceCode") + .HasPrincipalKey("CellularManagement.Domain.Entities.Device.CellularDevice", "DeviceCode") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.ProtocolVersion", b => + { + b.HasOne("CellularManagement.Domain.Entities.Device.CellularDevice", null) + .WithMany("ProtocolVersions") + .HasForeignKey("SerialNumber") + .HasPrincipalKey("SerialNumber") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.LoginLog", b => + { + b.HasOne("CellularManagement.Domain.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.RAN_Configuration", null) + .WithMany() + .HasForeignKey("RanId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.Stack_CoreIMS_Binding", b => + { + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.CoreNetworkConfig", "CoreNetworkConfig") + .WithMany() + .HasForeignKey("CnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.IMS_Configuration", "IMSConfiguration") + .WithMany() + .HasForeignKey("ImsId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", "NetworkStackConfig") + .WithMany("StackCoreIMSBindings") + .HasForeignKey("NetworkStackConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CoreNetworkConfig"); + + b.Navigation("IMSConfiguration"); + + b.Navigation("NetworkStackConfig"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => + { + b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") + .WithMany("RolePermissions") + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => + { + b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDevice", b => + { + b.Navigation("ProtocolVersions"); + + b.Navigation("Runtime"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.Navigation("StackCoreIMSBindings"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => + { + b.Navigation("RolePermissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/X1.Infrastructure/Migrations/20250820020118_UpdateAdbOperationPathNullable.cs b/src/X1.Infrastructure/Migrations/20250820020118_UpdateAdbOperationPathNullable.cs new file mode 100644 index 0000000..8e3c938 --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250820020118_UpdateAdbOperationPathNullable.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + /// + public partial class UpdateAdbOperationPathNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Path", + table: "tb_adboperations", + type: "character varying(500)", + maxLength: 500, + nullable: true, + comment: "命令执行时所依赖的路径(当启用绝对路径时必填)", + oldClrType: typeof(string), + oldType: "character varying(500)", + oldMaxLength: 500, + oldComment: "命令执行时所依赖的路径"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Path", + table: "tb_adboperations", + type: "character varying(500)", + maxLength: 500, + nullable: false, + defaultValue: "", + comment: "命令执行时所依赖的路径", + oldClrType: typeof(string), + oldType: "character varying(500)", + oldMaxLength: 500, + oldNullable: true, + oldComment: "命令执行时所依赖的路径(当启用绝对路径时必填)"); + } + } +} diff --git a/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs index 661aa09..a31c427 100644 --- a/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs +++ b/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs @@ -1034,10 +1034,9 @@ namespace X1.Infrastructure.Migrations .HasComment("是否启用"); b.Property("Path") - .IsRequired() .HasMaxLength(500) .HasColumnType("character varying(500)") - .HasComment("命令执行时所依赖的路径"); + .HasComment("命令执行时所依赖的路径(当启用绝对路径时必填)"); b.Property("ScreenshotData") .HasColumnType("BYTEA") diff --git a/src/X1.WebAPI/Properties/launchSettings.json b/src/X1.WebAPI/Properties/launchSettings.json index 9a408c6..8ba8425 100644 --- a/src/X1.WebAPI/Properties/launchSettings.json +++ b/src/X1.WebAPI/Properties/launchSettings.json @@ -24,8 +24,8 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "swagger", - "applicationUrl": "https://localhost:7268;http://localhost:5000;https://192.168.1.233:7268;http://192.168.1.233:5000", - //"applicationUrl": "https://localhost:7268;http://localhost:5000", + //"applicationUrl": "https://localhost:7268;http://localhost:5000;https://192.168.1.233:7268;http://192.168.1.233:5000", + "applicationUrl": "https://localhost:7268;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/X1.WebUI/package-lock.json b/src/X1.WebUI/package-lock.json index 90c0529..1d54e7d 100644 --- a/src/X1.WebUI/package-lock.json +++ b/src/X1.WebUI/package-lock.json @@ -48,6 +48,7 @@ "react-hook-form": "^7.50.0", "react-icons": "^5.5.0", "react-router-dom": "^6.22.0", + "reactflow": "^11.11.4", "recoil": "^0.7.7", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", @@ -2023,6 +2024,102 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, + "node_modules/@reactflow/background": { + "version": "11.3.14", + "resolved": "https://registry.npmmirror.com/@reactflow/background/-/background-11.3.14.tgz", + "integrity": "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls": { + "version": "11.2.14", + "resolved": "https://registry.npmmirror.com/@reactflow/controls/-/controls-11.2.14.tgz", + "integrity": "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/core": { + "version": "11.11.4", + "resolved": "https://registry.npmmirror.com/@reactflow/core/-/core-11.11.4.tgz", + "integrity": "sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==", + "dependencies": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/minimap": { + "version": "11.7.14", + "resolved": "https://registry.npmmirror.com/@reactflow/minimap/-/minimap-11.7.14.tgz", + "integrity": "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==", + "dependencies": { + "@reactflow/core": "11.11.4", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.2.14", + "resolved": "https://registry.npmmirror.com/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz", + "integrity": "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.3.14", + "resolved": "https://registry.npmmirror.com/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz", + "integrity": "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/@remix-run/router": { "version": "1.23.0", "resolved": "https://registry.npmmirror.com/@remix-run/router/-/router-1.23.0.tgz", @@ -2136,6 +2233,228 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmmirror.com/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmmirror.com/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmmirror.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmmirror.com/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmmirror.com/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", @@ -2143,6 +2462,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2862,6 +3186,11 @@ "url": "https://polar.sh/cva" } }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmmirror.com/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" + }, "node_modules/cli-cursor": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-4.0.0.tgz", @@ -2997,6 +3326,102 @@ "devOptional": true, "license": "MIT" }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -5366,6 +5791,23 @@ } } }, + "node_modules/reactflow": { + "version": "11.11.4", + "resolved": "https://registry.npmmirror.com/reactflow/-/reactflow-11.11.4.tgz", + "integrity": "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==", + "dependencies": { + "@reactflow/background": "11.3.14", + "@reactflow/controls": "11.2.14", + "@reactflow/core": "11.11.4", + "@reactflow/minimap": "11.7.14", + "@reactflow/node-resizer": "2.2.14", + "@reactflow/node-toolbar": "1.3.14" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", @@ -6429,6 +6871,33 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/src/X1.WebUI/src/constants/api.ts b/src/X1.WebUI/src/constants/api.ts index e8c5f70..1694848 100644 --- a/src/X1.WebUI/src/constants/api.ts +++ b/src/X1.WebUI/src/constants/api.ts @@ -9,6 +9,9 @@ export const API_PATHS = { // AT操作相关 AT_OPERATIONS: '/at-operations', + // ADB操作相关 + ADB_OPERATIONS: '/adb-operations', + // 协议相关 PROTOCOLS: '/protocolversions', PROTOCOL_LOGS: '/protocolLogs', diff --git a/src/X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx b/src/X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx new file mode 100644 index 0000000..f3804fe --- /dev/null +++ b/src/X1.WebUI/src/pages/adb-operations/AdbOperationDrawer.tsx @@ -0,0 +1,442 @@ +import React, { useEffect, useState } from 'react'; +import { AdbOperation, CreateAdbOperationRequest, UpdateAdbOperationRequest } from '@/services/adbOperationsService'; +import { Drawer, DrawerHeader, DrawerContent, DrawerFooter } from '@/components/ui/drawer'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Textarea } from '@/components/ui/textarea'; +import { Checkbox } from '@/components/ui/checkbox'; +import { getTerminalDevices, TerminalDevice } from '@/services/terminalDeviceService'; +import { Plus, Trash2, X } from 'lucide-react'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; + +interface CommandItem { + command: string; + waitTimeMs: number; +} + +interface AdbOperationDrawerProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onSubmit: (data: CreateAdbOperationRequest | UpdateAdbOperationRequest) => Promise; + initialData?: AdbOperation; + isEdit?: boolean; + isSubmitting?: boolean; +} + +export default function AdbOperationDrawer({ + open, + onOpenChange, + onSubmit, + initialData, + isEdit = false, + isSubmitting = false +}: AdbOperationDrawerProps) { + // 解析初始命令数据 + const parseInitialCommands = (): CommandItem[] => { + if (initialData?.command) { + try { + return JSON.parse(initialData.command); + } catch { + // 如果不是JSON格式,当作单条命令处理 + return [{ command: initialData.command, waitTimeMs: initialData.waitTimeMs || 0 }]; + } + } + return [{ command: '', waitTimeMs: 0 }]; + }; + + const [commands, setCommands] = useState(parseInitialCommands()); + const [formData, setFormData] = useState({ + command: '', + description: '', + path: '', + useAbsolutePath: false, + isEnabled: true, + waitTimeMs: 0, + deviceId: '' + }); + + const [errors, setErrors] = useState>({}); + const [devices, setDevices] = useState([]); + const [loadingDevices, setLoadingDevices] = useState(false); + + // 当抽屉打开时,初始化表单数据 + useEffect(() => { + if (open) { + if (initialData && isEdit) { + const parsedCommands = parseInitialCommands(); + setCommands(parsedCommands); + setFormData({ + command: initialData.command || '', + description: initialData.description || '', + path: initialData.path || '', + useAbsolutePath: initialData.useAbsolutePath ?? false, + isEnabled: initialData.isEnabled ?? true, + waitTimeMs: initialData.waitTimeMs || 0, + deviceId: initialData.deviceId || '' + }); + } else { + // 重置表单 + setCommands([{ command: '', waitTimeMs: 0 }]); + setFormData({ + command: '', + description: '', + path: '', + useAbsolutePath: false, + isEnabled: true, + waitTimeMs: 0, + deviceId: '' + }); + } + setErrors({}); + } + }, [open, initialData, isEdit]); + + // 加载设备列表 + useEffect(() => { + if (open) { + const loadDevices = async () => { + setLoadingDevices(true); + try { + const result = await getTerminalDevices({ pageSize: 100 }); // 获取所有设备 + if (result.isSuccess && result.data) { + setDevices(result.data.terminalDevices || []); + } + } catch (error) { + console.error('加载设备列表失败:', error); + } finally { + setLoadingDevices(false); + } + }; + + loadDevices(); + } + }, [open]); + + // 更新命令列表 + const updateCommands = (newCommands: CommandItem[]) => { + setCommands(newCommands); + // 将命令列表转换为JSON字符串 + const commandJson = JSON.stringify(newCommands); + setFormData(prev => ({ ...prev, command: commandJson })); + }; + + // 添加新命令 + const addCommand = () => { + updateCommands([...commands, { command: '', waitTimeMs: 0 }]); + }; + + // 删除命令 + const removeCommand = (index: number) => { + if (commands.length > 1) { + const newCommands = commands.filter((_, i) => i !== index); + updateCommands(newCommands); + } + }; + + // 更新单个命令 + const updateCommand = (index: number, field: keyof CommandItem, value: string | number) => { + const newCommands = [...commands]; + newCommands[index] = { ...newCommands[index], [field]: value }; + updateCommands(newCommands); + }; + + // 表单验证 + const validateForm = (): boolean => { + const newErrors: Record = {}; + + // 验证设备ID + if (!formData.deviceId.trim()) { + newErrors.deviceId = '请选择设备'; + } + + // 验证命令列表 + if (commands.length === 0) { + newErrors.commands = '至少需要一条ADB命令'; + } else { + for (let i = 0; i < commands.length; i++) { + if (!commands[i].command.trim()) { + newErrors[`command_${i}`] = `第${i + 1}条命令不能为空`; + } + if (commands[i].waitTimeMs < 0) { + newErrors[`waitTime_${i}`] = `第${i + 1}条命令的等待时间不能为负数`; + } + } + } + + // 验证ADB路径 - 只有在使用绝对路径时才必填 + if (formData.useAbsolutePath && !formData.path.trim()) { + newErrors.path = '使用绝对路径时必须指定ADB路径'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (isSubmitting) return; + + if (!validateForm()) { + return; + } + + // 提交前清理数据 + const submitData = { + ...formData, + command: formData.command, // 已经是JSON字符串 + description: formData.description.trim(), + path: formData.path.trim() + }; + + await onSubmit(submitData); + }; + + const handleInputChange = (field: keyof CreateAdbOperationRequest, value: string | number | boolean) => { + setFormData(prev => ({ ...prev, [field]: value })); + + // 清除对应字段的错误 + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: '' })); + } + }; + + return ( + +
+ +
+

+ {isEdit ? '编辑ADB操作' : '创建ADB操作'} +

+ +
+
+ +
+ + {/* 固定区域:设备选择 */} +
+ + + {errors.deviceId && ( +

{errors.deviceId}

+ )} +

+ 选择要执行ADB操作的设备,设备ID将与TerminalDevice的Serial字段关联 +

+
+ + {/* 命令列表区域 */} +
2 ? 'flex-1 min-h-0 flex flex-col' : ''}`}> +
+ + +
+ + {errors.commands && ( +

{errors.commands}

+ )} + + {/* 命令列表区域 - 根据命令数量决定是否可滚动 */} +
2 ? 'flex-1 overflow-y-auto' : ''}> +
2 ? 'pr-2' : ''}`}> + {commands.map((cmd, index) => ( +
+
+
+ + updateCommand(index, 'command', e.target.value)} + placeholder="例如: adb devices, adb shell ls, adb install app.apk" + disabled={isSubmitting} + className={errors[`command_${index}`] ? 'border-red-500 focus:border-red-500' : ''} + /> + {errors[`command_${index}`] && ( +

{errors[`command_${index}`]}

+ )} +
+ +
+ + updateCommand(index, 'waitTimeMs', parseInt(e.target.value) || 0)} + placeholder="例如: 1000" + disabled={isSubmitting} + className={errors[`waitTime_${index}`] ? 'border-red-500 focus:border-red-500' : ''} + /> + {errors[`waitTime_${index}`] && ( +

{errors[`waitTime_${index}`]}

+ )} +
+
+ + {commands.length > 1 && ( + + )} +
+ ))} +
+
+ +

+ 可以添加多条ADB命令,系统将按顺序执行。每条命令可以设置独立的等待时间。 +

+
+ + {/* 固定区域:其他表单字段 */} +
+ {/* 操作描述 */} +
+ +