diff --git a/Apimanager_backend/Config/MyAutomapper.cs b/Apimanager_backend/Config/MyAutomapper.cs index aaccdb9..28b1733 100644 --- a/Apimanager_backend/Config/MyAutomapper.cs +++ b/Apimanager_backend/Config/MyAutomapper.cs @@ -11,6 +11,8 @@ namespace Apimanager_backend.Config CreateMap(); CreateMap() .ForMember(dest => dest.PassHash, opt => opt.MapFrom(src => src.Password)); + CreateMap(); + CreateMap(); } } } diff --git a/Apimanager_backend/Controllers/AdminController.cs b/Apimanager_backend/Controllers/AdminController.cs index 6d04c07..0a752c4 100644 --- a/Apimanager_backend/Controllers/AdminController.cs +++ b/Apimanager_backend/Controllers/AdminController.cs @@ -100,6 +100,7 @@ namespace Apimanager_backend.Controllers message:"Success", data:null ); + return Ok(res); } #endregion #region 更新用户信息 @@ -115,6 +116,7 @@ namespace Apimanager_backend.Controllers message: "Success", data: userInfo ); + return Ok(res); } catch(BaseException e) { @@ -129,5 +131,19 @@ namespace Apimanager_backend.Controllers } #endregion + #region 用户数量 + [HttpGet] + [Authorize(Roles = "Admin")] + public async Task>> UserCount() + { + int count = await adminService.UserCountAsync(); + var res = new ResponseBase( + code:1000, + message:"Success", + data:count + ); + return Ok(res); + } + #endregion } } diff --git a/Apimanager_backend/Controllers/ApisController.cs b/Apimanager_backend/Controllers/ApisController.cs new file mode 100644 index 0000000..090b8da --- /dev/null +++ b/Apimanager_backend/Controllers/ApisController.cs @@ -0,0 +1,179 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using StackExchange.Redis; + +namespace Apimanager_backend.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + public class ApisController : ControllerBase + { + private readonly IApiService apiService; + private readonly ILogger logger; + public ApisController(IApiService apiService,ILogger logger) + { + this.apiService = apiService; + this.logger = logger; + } + #region 查询API信息 + [HttpGet] + [Authorize(Roles = "User")] + public async Task>> ApiInfo(int apiId) + { + try + { + var apiInfo = await apiService.GetApiInfoAsync(apiId); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: apiInfo + ); + return Ok(res); + }catch(BaseException e) + { + var res = new ResponseBase( + code: e.code, + message: e.message, + data: null + ); + return NotFound(res); + } + + } + #endregion + #region 查询API列表 + [HttpGet] + [Authorize(Roles = "User")] + public async Task>>> ApiList(int pageIndex,int pageSize,bool desc) + { + var list = await apiService.GetApisAsync(pageIndex, pageSize, desc); + var res = new ResponseBase>( + code:1000, + message:"Success", + data:list + ); + return Ok(res); + } + #endregion + #region 添加接口 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> AddApi([FromBody]CreateApiInfo createApiInfo) + { + var apiInfo = await apiService.AddApiAsync(createApiInfo); + var res = new ResponseBase( + code:1000, + message:"Success", + data: apiInfo + ); + return Ok(res); + } + #endregion + #region 删除接口 + [HttpDelete] + [Authorize(Roles = "Admin")] + public async Task>> DeleteApi(int apiId) + { + try + { + await apiService.DeleteApiAsync(apiId); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: null + ); + return Ok(res); + }catch(BaseException e) + { + var res = new ResponseBase( + code: e.code, + message: e.message, + data: null + ); + return NotFound(res); + } + } + #endregion + #region 更新api信息 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> UpdateApi(int apiId,UpdateApiDto updateApiDto) + { + try + { + //更新 + var apiInfo = await apiService.UpdateApiAsync(apiId, updateApiDto); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: apiInfo + ); + return Ok(res); + }catch(BaseException e) + { + var res = new ResponseBase( + code: e.code, + message: e.message, + data: null + ); + return NotFound(res); + } + + } + #endregion + #region 禁用APi + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> BanApi(int userId) + { + try + { + await apiService.OffApiAsync(userId); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: null + ); + return Ok(res); + }catch(BaseException e) + { + var res = new ResponseBase( + code: e.code, + message: e.message, + data: null + ); + return NotFound(res); + } + } + #endregion + #region 启用API + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> UnBanApi(int userId) + { + try + { + await apiService.OnApiAsync(userId); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: null + ); + return Ok(res); + }catch(BaseException e) + { + var res = new ResponseBase( + code: e.code, + message: e.message, + data: null + ); + return NotFound(res); + } + } + #endregion + } +} diff --git a/Apimanager_backend/Data/ApiRequestExampleConfig.cs b/Apimanager_backend/Data/ApiRequestExampleConfig.cs new file mode 100644 index 0000000..52ab3cd --- /dev/null +++ b/Apimanager_backend/Data/ApiRequestExampleConfig.cs @@ -0,0 +1,19 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Apimanager_backend.Data +{ + public class ApiRequestExampleConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + //主键 + builder.HasKey(x => x.Id); + //外键 + builder.HasOne(x => x.Api) + .WithMany(u => u.ApiRequestExamples) + .HasForeignKey(x => x.ApiId); + } + } +} diff --git a/Apimanager_backend/Dtos/AddPackageDto.cs b/Apimanager_backend/Dtos/AddPackageDto.cs new file mode 100644 index 0000000..20e1739 --- /dev/null +++ b/Apimanager_backend/Dtos/AddPackageDto.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Dtos +{ + public class AddPackageDto + { + [Required(ErrorMessage = "套餐名称必填")] + [MaxLength(20,ErrorMessage = "套餐名称最大20字符")] + public string Name { get; set; } + [Required(ErrorMessage = "调用次数必填")] + public int CallLimit { get; set; } + [Required(ErrorMessage = "价格必填")] + public decimal Price { get; set; } + [Required(ErrorMessage = "分钟调用限制必填")] + public int OneMinuteLimit { get; set; } + } +} diff --git a/Apimanager_backend/Dtos/AdminUpdateUserDto.cs b/Apimanager_backend/Dtos/AdminUpdateUserDto.cs index e82b1e7..f084d72 100644 --- a/Apimanager_backend/Dtos/AdminUpdateUserDto.cs +++ b/Apimanager_backend/Dtos/AdminUpdateUserDto.cs @@ -2,7 +2,7 @@ { public class AdminUpdateUserDto { - public string Password { get; set; } - public decimal Balance { get; set; } + public string? Password { get; set; } + public decimal? Balance { get; set; } } } diff --git a/Apimanager_backend/Dtos/ApiInfoDto.cs b/Apimanager_backend/Dtos/ApiInfoDto.cs new file mode 100644 index 0000000..85cde25 --- /dev/null +++ b/Apimanager_backend/Dtos/ApiInfoDto.cs @@ -0,0 +1,16 @@ +using Apimanager_backend.Models; + +namespace Apimanager_backend.Dtos +{ + public class ApiInfoDto + { + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string Endpoint { get; set; } + public ApiMethod Method { get; set; } + public bool IsThirdParty { get; set; } + public bool IsActive { get; set; } + public Apipackage? Package { get; set; } + } +} diff --git a/Apimanager_backend/Dtos/ResponseBase.cs b/Apimanager_backend/Dtos/Base/ResponseBase.cs similarity index 78% rename from Apimanager_backend/Dtos/ResponseBase.cs rename to Apimanager_backend/Dtos/Base/ResponseBase.cs index 94d94f9..daa2563 100644 --- a/Apimanager_backend/Dtos/ResponseBase.cs +++ b/Apimanager_backend/Dtos/Base/ResponseBase.cs @@ -15,6 +15,12 @@ this.Message = message; this.Data = data; } + public ResponseBase(T data) + { + this.Code = 1000; + this.Message = "Success"; + this.Data = data; + } public ResponseBase() { } } } diff --git a/Apimanager_backend/Dtos/CreateApiInfo.cs b/Apimanager_backend/Dtos/CreateApiInfo.cs new file mode 100644 index 0000000..1f8ba6d --- /dev/null +++ b/Apimanager_backend/Dtos/CreateApiInfo.cs @@ -0,0 +1,22 @@ +using Apimanager_backend.Models; +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Dtos +{ + public class CreateApiInfo + { + [Required(ErrorMessage = "API名称必填!")] + [MaxLength(50,ErrorMessage = "API名称最大50字符")] + public string Name { get; set; } + public string Description { get; set; } = string.Empty; + [Required(ErrorMessage = "调用端点必填")] + public string Endpoint { get; set; } + [Required(ErrorMessage = "调用方式必填")] + [MaxLength(20,ErrorMessage = "调用方式最大20字符")] + public ApiMethod Method { get; set; } + public int? PackageId { get; set; } + [Required(ErrorMessage = "是否为三方接口必选")] + public bool IsThirdParty { get; set; } + + } +} diff --git a/Apimanager_backend/Dtos/PackageInfoDto.cs b/Apimanager_backend/Dtos/PackageInfoDto.cs new file mode 100644 index 0000000..2a8795b --- /dev/null +++ b/Apimanager_backend/Dtos/PackageInfoDto.cs @@ -0,0 +1,12 @@ +namespace Apimanager_backend.Dtos +{ + public class PackageInfoDto + { + public int Id { get; set; } + public string Name { get; set; } + public int CallLimit { get; set; } + public decimal Price { get; set; } + public int OneMinuteLimit { get; set; } + + } +} diff --git a/Apimanager_backend/Dtos/UpdateApiDto.cs b/Apimanager_backend/Dtos/UpdateApiDto.cs new file mode 100644 index 0000000..de73920 --- /dev/null +++ b/Apimanager_backend/Dtos/UpdateApiDto.cs @@ -0,0 +1,14 @@ +using Apimanager_backend.Models; + +namespace Apimanager_backend.Dtos +{ + public class UpdateApiDto + { + public string? Name { get; set; } + public string? Description { get; set; } + public string? Endpoint { get; set; } + public ApiMethod? Method { get; set; } + public int? PackageId { get; set; } + + } +} diff --git a/Apimanager_backend/Dtos/UpdatePackageDto.cs b/Apimanager_backend/Dtos/UpdatePackageDto.cs new file mode 100644 index 0000000..0fea859 --- /dev/null +++ b/Apimanager_backend/Dtos/UpdatePackageDto.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Dtos +{ + public class UpdatePackageDto + { + [MaxLength(20, ErrorMessage = "套餐名称最大20字符")] + public string? Name { get; set; } + public int? CallLimit { get; set; } + public decimal? Price { get; set; } + public int? OneMinuteLimit { get; set; } + } +} diff --git a/Apimanager_backend/Migrations/20241108013827_add-apirequestExampleTable.Designer.cs b/Apimanager_backend/Migrations/20241108013827_add-apirequestExampleTable.Designer.cs new file mode 100644 index 0000000..d57e635 --- /dev/null +++ b/Apimanager_backend/Migrations/20241108013827_add-apirequestExampleTable.Designer.cs @@ -0,0 +1,493 @@ +// +using System; +using Apimanager_backend.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Apimanager_backend.Migrations +{ + [DbContext(typeof(ApiContext))] + [Migration("20241108013827_add-apirequestExampleTable")] + partial class addapirequestExampleTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Apimanager_backend.Models.Api", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Endpoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsDelete") + .HasColumnType("tinyint(1)"); + + b.Property("IsThirdParty") + .HasColumnType("tinyint(1)"); + + b.Property("Method") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PackageId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.ToTable("Apis"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiId") + .HasColumnType("int"); + + b.Property("CallResult") + .HasColumnType("int"); + + b.Property("CallTime") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ApiId"); + + b.HasIndex("UserId"); + + b.ToTable("CallLogs"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.ApiRequestExample", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiId") + .HasColumnType("int"); + + b.Property("Request") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Response") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ResponseType") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiId"); + + b.ToTable("ApiRequestExample"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CallLimit") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("OneMinuteLimit") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.ToTable("Apipackages"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.Log", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Exception") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LogLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MessageTemplate") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Logs"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("varchar(45)"); + + b.Property("Operation") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TargetId") + .HasColumnType("int"); + + b.Property("TargetType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UserAgent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("OperationLogs"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("OrderNumber") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("OrderType") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("ThirdPartyOrderId") + .HasColumnType("varchar(255)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderNumber") + .IsUnique(); + + b.HasIndex("ThirdPartyOrderId") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .HasColumnType("longtext"); + + b.Property("Balance") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("IsBan") + .HasColumnType("tinyint(1)"); + + b.Property("IsDelete") + .HasColumnType("tinyint(1)"); + + b.Property("PassHash") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.UserPackage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PackageId") + .HasColumnType("int"); + + b.Property("PurchasedAt") + .HasColumnType("datetime(6)"); + + b.Property("RemainingCalls") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("UserId"); + + b.ToTable("UserPackages"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.UserRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Role") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserRoles"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.Api", b => + { + b.HasOne("Apimanager_backend.Models.Apipackage", "Package") + .WithMany("Apis") + .HasForeignKey("PackageId"); + + b.Navigation("Package"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.ApiCallLog", b => + { + b.HasOne("Apimanager_backend.Models.Api", "Api") + .WithMany("ApiCalls") + .HasForeignKey("ApiId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Apimanager_backend.Models.User", "User") + .WithMany("CallLogs") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Api"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.ApiRequestExample", b => + { + b.HasOne("Apimanager_backend.Models.Api", "Api") + .WithMany("ApiRequestExamples") + .HasForeignKey("ApiId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Api"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b => + { + b.HasOne("Apimanager_backend.Models.User", "User") + .WithMany("operationLogs") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.Order", b => + { + b.HasOne("Apimanager_backend.Models.User", "User") + .WithMany("Orders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.UserPackage", b => + { + b.HasOne("Apimanager_backend.Models.Apipackage", "Package") + .WithMany("Packages") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Apimanager_backend.Models.User", "User") + .WithMany("Packages") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.UserRole", b => + { + b.HasOne("Apimanager_backend.Models.User", "User") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.Api", b => + { + b.Navigation("ApiCalls"); + + b.Navigation("ApiRequestExamples"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b => + { + b.Navigation("Apis"); + + b.Navigation("Packages"); + }); + + modelBuilder.Entity("Apimanager_backend.Models.User", b => + { + b.Navigation("CallLogs"); + + b.Navigation("Orders"); + + b.Navigation("Packages"); + + b.Navigation("Roles"); + + b.Navigation("operationLogs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Apimanager_backend/Migrations/20241108013827_add-apirequestExampleTable.cs b/Apimanager_backend/Migrations/20241108013827_add-apirequestExampleTable.cs new file mode 100644 index 0000000..e252d58 --- /dev/null +++ b/Apimanager_backend/Migrations/20241108013827_add-apirequestExampleTable.cs @@ -0,0 +1,86 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Apimanager_backend.Migrations +{ + /// + public partial class addapirequestExampleTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ApiKey", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Description", + table: "Apis", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "OneMinuteLimit", + table: "Apipackages", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "ApiRequestExample", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ApiId = table.Column(type: "int", nullable: false), + ResponseType = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Request = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Response = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ApiRequestExample", x => x.Id); + table.ForeignKey( + name: "FK_ApiRequestExample_Apis_ApiId", + column: x => x.ApiId, + principalTable: "Apis", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_ApiRequestExample_ApiId", + table: "ApiRequestExample", + column: "ApiId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApiRequestExample"); + + migrationBuilder.DropColumn( + name: "ApiKey", + table: "Users"); + + migrationBuilder.DropColumn( + name: "Description", + table: "Apis"); + + migrationBuilder.DropColumn( + name: "OneMinuteLimit", + table: "Apipackages"); + } + } +} diff --git a/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs b/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs index 06c0db0..bd0a686 100644 --- a/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs +++ b/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs @@ -28,6 +28,10 @@ namespace Apimanager_backend.Migrations b.Property("CreatedAt") .HasColumnType("datetime(6)"); + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + b.Property("Endpoint") .IsRequired() .HasColumnType("longtext"); @@ -86,6 +90,34 @@ namespace Apimanager_backend.Migrations b.ToTable("CallLogs"); }); + modelBuilder.Entity("Apimanager_backend.Models.ApiRequestExample", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiId") + .HasColumnType("int"); + + b.Property("Request") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Response") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ResponseType") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiId"); + + b.ToTable("ApiRequestExample"); + }); + modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b => { b.Property("Id") @@ -106,6 +138,9 @@ namespace Apimanager_backend.Migrations .HasMaxLength(20) .HasColumnType("varchar(20)"); + b.Property("OneMinuteLimit") + .HasColumnType("int"); + b.Property("Price") .HasColumnType("decimal(65,30)"); @@ -246,6 +281,9 @@ namespace Apimanager_backend.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("ApiKey") + .HasColumnType("longtext"); + b.Property("Balance") .HasColumnType("decimal(65,30)"); @@ -357,6 +395,17 @@ namespace Apimanager_backend.Migrations b.Navigation("User"); }); + modelBuilder.Entity("Apimanager_backend.Models.ApiRequestExample", b => + { + b.HasOne("Apimanager_backend.Models.Api", "Api") + .WithMany("ApiRequestExamples") + .HasForeignKey("ApiId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Api"); + }); + modelBuilder.Entity("Apimanager_backend.Models.OperationLog", b => { b.HasOne("Apimanager_backend.Models.User", "User") @@ -412,6 +461,8 @@ namespace Apimanager_backend.Migrations modelBuilder.Entity("Apimanager_backend.Models.Api", b => { b.Navigation("ApiCalls"); + + b.Navigation("ApiRequestExamples"); }); modelBuilder.Entity("Apimanager_backend.Models.Apipackage", b => diff --git a/Apimanager_backend/Models/Api.cs b/Apimanager_backend/Models/Api.cs index 85b8d23..be2752a 100644 --- a/Apimanager_backend/Models/Api.cs +++ b/Apimanager_backend/Models/Api.cs @@ -16,6 +16,10 @@ namespace Apimanager_backend.Models [MaxLength(200)] [Required] public string Name { get; set; } // varchar(20) + /// + /// 描述 + /// + public string Description { get; set; } = string.Empty; /// /// API地址 @@ -55,5 +59,6 @@ namespace Apimanager_backend.Models //导航属性 public Apipackage? Package { get; set; } public ICollection ApiCalls { get; set; } + public ICollection ApiRequestExamples { get; set; } } } diff --git a/Apimanager_backend/Models/ApiMethod.cs b/Apimanager_backend/Models/ApiMethod.cs index 2d736c9..c709c94 100644 --- a/Apimanager_backend/Models/ApiMethod.cs +++ b/Apimanager_backend/Models/ApiMethod.cs @@ -2,9 +2,9 @@ { public enum ApiMethod { - GET, - POST, - PUT, - DELETE + GET = 0, + POST = 1, + PUT = 2, + DELETE = 3 } } diff --git a/Apimanager_backend/Models/ApiRequestExample.cs b/Apimanager_backend/Models/ApiRequestExample.cs new file mode 100644 index 0000000..2203511 --- /dev/null +++ b/Apimanager_backend/Models/ApiRequestExample.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Models +{ + public class ApiRequestExample + { + public int Id { get; set; } + public int ApiId { get; set; } + [Required] + public string ResponseType { get; set; } + public string Request { get; set; } = string.Empty; + public string Response { get; set; } = string.Empty; + //导航属性 + public Api Api { get; set; } + } +} diff --git a/Apimanager_backend/Models/Apipackage.cs b/Apimanager_backend/Models/Apipackage.cs index fc031ba..4e9eed3 100644 --- a/Apimanager_backend/Models/Apipackage.cs +++ b/Apimanager_backend/Models/Apipackage.cs @@ -27,10 +27,11 @@ namespace Apimanager_backend.Models /// public decimal Price { get; set; } // decimal(10,2) + /// - /// 套餐过期时间,可用于控制套餐是否过期 + /// 每分钟调用次数限制 /// - public DateTime ExpiryDate { get; set; } // timestamp + public int OneMinuteLimit { get; set; } /// /// 创建时间 diff --git a/Apimanager_backend/Models/User.cs b/Apimanager_backend/Models/User.cs index 0366bea..1b4a180 100644 --- a/Apimanager_backend/Models/User.cs +++ b/Apimanager_backend/Models/User.cs @@ -45,6 +45,10 @@ namespace Apimanager_backend.Models /// 余额 /// public decimal Balance { get; set; } = 0; // Decimal(10) + /// + /// api调用凭证 + /// + public string? ApiKey { get; set; } = null; /// /// 创建时间,默认当前时间 diff --git a/Apimanager_backend/Models/UserPackage.cs b/Apimanager_backend/Models/UserPackage.cs index bee6120..f94b7f5 100644 --- a/Apimanager_backend/Models/UserPackage.cs +++ b/Apimanager_backend/Models/UserPackage.cs @@ -20,7 +20,10 @@ /// 剩余调用次数 /// public int RemainingCalls { get; set; } - + /// + /// 套餐过期时间,可用于控制套餐是否过期 + /// + public DateTime ExpiryDate { get; set; } // timestamp /// /// 购买时间 /// diff --git a/Apimanager_backend/Services/AdminService.cs b/Apimanager_backend/Services/AdminService.cs index 5cd7898..bfbcb43 100644 --- a/Apimanager_backend/Services/AdminService.cs +++ b/Apimanager_backend/Services/AdminService.cs @@ -101,12 +101,18 @@ namespace Apimanager_backend.Services { throw new BaseException(2004,"用户不存在"); } - user.PassHash = dto.Password; - user.Balance = dto.Balance; + user.PassHash = dto.Password ?? user.PassHash; + user.Balance = dto.Balance ?? user.Balance; context.Users.Update(user); await context.SaveChangesAsync(); return mapper.Map(user); } #endregion + #region 用户总数 + public async Task UserCountAsync() + { + return await context.Users.CountAsync(); + } + #endregion } } diff --git a/Apimanager_backend/Services/ApiService.cs b/Apimanager_backend/Services/ApiService.cs new file mode 100644 index 0000000..d8604e7 --- /dev/null +++ b/Apimanager_backend/Services/ApiService.cs @@ -0,0 +1,120 @@ +using Apimanager_backend.Data; +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Models; +using AutoMapper; +using Microsoft.EntityFrameworkCore; + +namespace Apimanager_backend.Services +{ + public class ApiService:IApiService + { + private readonly ApiContext context; + private readonly ILogger logger; + private readonly IMapper mapper; + public ApiService(ApiContext context, ILogger logger,IMapper mapper) + { + this.context = context; + this.logger = logger; + this.mapper = mapper; + } + #region 添加接口 + public async Task AddApiAsync(CreateApiInfo dto) + { + var api = mapper.Map(dto); + context.Apis.Add(api); + await context.SaveChangesAsync(); + return mapper.Map(api); + } + #endregion + #region 删除api + public async Task DeleteApiAsync(int apiId) + { + var api = await context.Apis.FirstOrDefaultAsync(x => x.Id == apiId); + if (api == null) + { + throw new BaseException(3002, "API不存在"); + } + api.IsDelete = true; + context.Apis.Update(api); + await context.SaveChangesAsync(); + } + #endregion + #region 查询api信息 + public async Task GetApiInfoAsync(int apiId) + { + var apiInfo = await context.Apis.FirstOrDefaultAsync(x => x.Id == apiId); + if (apiInfo == null) + { + throw new BaseException(3002,"API不存在"); + } + return mapper.Map(apiInfo); + } + #endregion + #region 禁用api + public async Task OffApiAsync(int apiId) + { + var api = await context.Apis.FirstOrDefaultAsync(x => x.Id == apiId); + if (api == null) + { + throw new BaseException(3002, "API不存在"); + } + api.IsActive = false; + context.Apis.Update(api); + await context.SaveChangesAsync(); + } + #endregion + #region 启用API + public async Task OnApiAsync(int apiId) + { + var api = await context.Apis.FirstOrDefaultAsync(x => x.Id == apiId); + if (api == null) + { + throw new BaseException(3002, "API不存在"); + } + api.IsActive = true; + context.Apis.Update(api); + await context.SaveChangesAsync(); + } + #endregion + #region 更新接口信息 + public async Task UpdateApiAsync(int apiId, UpdateApiDto dto) + { + var api = await context.Apis.FirstOrDefaultAsync(x => x.Id == apiId); + if(api == null) + { + throw new BaseException(3002, "API不存在"); + } + api.Name = dto.Name ?? api.Name; + api.Description = dto.Description ?? api.Description; + api.Endpoint = dto.Endpoint ?? api.Endpoint; + api.Method = dto.Method ?? api.Method; + api.PackageId = dto.PackageId ?? api.PackageId; + context.Apis.Update(api); + await context.SaveChangesAsync(); + return mapper.Map(api); + } + #endregion + #region 获取API列表 + public async Task> GetApisAsync(int pageIndex, int pageSize, bool desc) + { + IQueryable query = context.Apis.Where(x => true).OrderBy(x => x.Id); + //倒序 + if (desc) + { + query.OrderDescending(); + } + //分页 + query = query.Skip((pageIndex - 1) * pageSize).Take(pageSize); + var list = await query.ToListAsync(); + return mapper.Map>(list); + } + #endregion + #region 获取Api总数 + public async Task ApiCountAsync() + { + return await context.Apis.CountAsync(); + } + #endregion + } +} diff --git a/Apimanager_backend/Services/IAdminService.cs b/Apimanager_backend/Services/IAdminService.cs index e19fb34..a9115b6 100644 --- a/Apimanager_backend/Services/IAdminService.cs +++ b/Apimanager_backend/Services/IAdminService.cs @@ -40,6 +40,11 @@ namespace Apimanager_backend.Services /// 修改用户信息 /// /// - Task UpdateUserAsync(int userId,AdminUpdateUserDto dto); + Task UpdateUserAsync(int userId, AdminUpdateUserDto dto); + /// + /// 用户总数 + /// + /// + Task UserCountAsync(); } } diff --git a/Apimanager_backend/Services/IApiService.cs b/Apimanager_backend/Services/IApiService.cs new file mode 100644 index 0000000..0f91d72 --- /dev/null +++ b/Apimanager_backend/Services/IApiService.cs @@ -0,0 +1,60 @@ +using Apimanager_backend.Dtos; + +namespace Apimanager_backend.Services +{ + public interface IApiService + { + /// + /// 获取api信息 + /// + /// + /// + public Task GetApiInfoAsync(int apiId); + /// + /// 添加api + /// + /// + /// + /// + public Task AddApiAsync(CreateApiInfo dto); + /// + /// 删除api + /// + /// + /// + public Task DeleteApiAsync(int apiId); + /// + /// 更新api信息 + /// + /// + /// + /// + public Task UpdateApiAsync(int apiId,UpdateApiDto dto); + /// + /// 启用 + /// + /// + /// + public Task OnApiAsync(int apiId); + /// + /// 禁用 + /// + /// + /// + public Task OffApiAsync(int apiId); + /// + /// 获取APi列表 + /// + /// + /// + /// + /// + public Task> GetApisAsync(int pageIndex, int pageSize, bool desc); + /// + /// 获取api数量 + /// + /// + public Task ApiCountAsync(); + + } +} diff --git a/Apimanager_backend/Services/IPackageService.cs b/Apimanager_backend/Services/IPackageService.cs new file mode 100644 index 0000000..1f883d6 --- /dev/null +++ b/Apimanager_backend/Services/IPackageService.cs @@ -0,0 +1,43 @@ +using Apimanager_backend.Dtos; + +namespace Apimanager_backend.Services +{ + public interface IPackageService + { + /// + /// 添加套餐 + /// + /// + /// + public Task AddPackageAsync(AddPackageDto addPackageDto); + /// + /// 更新套餐信息 + /// + /// + /// + /// + public Task UpdatePackageAsync(int packageId,UpdatePackageDto updatePackageDto); + /// + /// 删除套餐 + /// + /// + /// + public Task DeletePackageAsync(int packageId); + /// + /// 获取套餐列表 + /// + /// + /// + /// + /// + public Task> GetAllPackagesAsync(int pageIndex,int pageSize,bool desc); + /// + /// 获取套餐信息 + /// + /// + /// + public Task PackageInfoByIdAsync(int packageId); + + + } +}