diff --git a/Apimanager_backend/Config/MyAutomapper.cs b/Apimanager_backend/Config/MyAutomapper.cs index 28b1733..f0895eb 100644 --- a/Apimanager_backend/Config/MyAutomapper.cs +++ b/Apimanager_backend/Config/MyAutomapper.cs @@ -13,6 +13,8 @@ namespace Apimanager_backend.Config .ForMember(dest => dest.PassHash, opt => opt.MapFrom(src => src.Password)); CreateMap(); CreateMap(); + CreateMap(); + CreateMap(); } } } diff --git a/Apimanager_backend/Config/ServiceCollectionExtensions.cs b/Apimanager_backend/Config/ServiceCollectionExtensions.cs index 8d90732..8f0a524 100644 --- a/Apimanager_backend/Config/ServiceCollectionExtensions.cs +++ b/Apimanager_backend/Config/ServiceCollectionExtensions.cs @@ -14,10 +14,15 @@ namespace Apimanager_backend.Config { public static IServiceCollection AddAllService(this IServiceCollection services,IConfiguration configuration) { + services.AddCorsService(configuration); services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); services.AddJWTService(configuration); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -82,5 +87,18 @@ namespace Apimanager_backend.Config context.Response.StatusCode = StatusCodes.Status401Unauthorized; await context.Response.WriteAsync(JsonConvert.SerializeObject(res)); } + public static IServiceCollection AddCorsService(this IServiceCollection services,IConfiguration configuration1) + { + services.AddCors(option => + { + option.AddPolicy("AllowAll",builder => + { + builder.AllowAnyHeader(); + builder.AllowAnyMethod(); + builder.AllowAnyOrigin(); + }); + }); + return services; + } } } diff --git a/Apimanager_backend/Controllers/PackageController.cs b/Apimanager_backend/Controllers/PackageController.cs new file mode 100644 index 0000000..b3d9aae --- /dev/null +++ b/Apimanager_backend/Controllers/PackageController.cs @@ -0,0 +1,173 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Models; +using Apimanager_backend.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Apimanager_backend.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + public class PackageController : ControllerBase + { + private readonly IPackageService packageService; + private readonly ILogger logger; + public PackageController(IPackageService packageService,ILogger logger) + { + this.packageService = packageService; + this.logger = logger; + } + #region 新增套餐 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> AddPackage(AddPackageDto addPackageDto) + { + var packageInfo = await packageService.AddPackageAsync(addPackageDto); + var res = new ResponseBase(packageInfo); + return Ok(res); + } + #endregion + #region 删除套餐 + [HttpDelete] + [Authorize(Roles = "Admin")] + public async Task>> DeletePackage(int packageId) + { + try + { + await packageService.DeletePackageAsync(packageId); + var res = new ResponseBase(null); + return Ok(res); + }catch(BaseException e) + { + var res = new ResponseBase( + code:e.code, + message:e.message, + data:null + ); + return NotFound(res); + } + } + #endregion + #region 修改套餐信息 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> UpdatePackage(int packageId, UpdatePackageDto updatePackageDto) + { + try + { + var packageInfo = await packageService.UpdatePackageAsync(packageId, updatePackageDto); + var res = new ResponseBase(packageInfo); + return Ok(res); + } + catch (BaseException e) + { + var res = new ResponseBase( + code:e.code, + message:e.message, + data:null + ); + return NotFound(res); + } + } + #endregion + #region 获取套餐信息 + [HttpGet] + [Authorize(Roles = "User")] + public async Task>> GetPackageInfo(int packageId) + { + try + { + var packageInfo = await packageService.PackageInfoByIdAsync(packageId); + var res = new ResponseBase(packageInfo); + return Ok(res); + }catch(BaseException e) + { + var res = new ResponseBase( + code:e.code, + message:e.message, + data:null + ); + return NotFound(res); + } + } + #endregion + #region 获取套餐列表 + [HttpGet] + [Authorize(Roles = "User")] + public async Task>>> GetPackageList(int pageIndex,int pageSize,bool desc) + { + var packageList = await packageService.GetAllPackagesAsync(pageIndex,pageSize,desc); + var res = new ResponseBase>(packageList); + return Ok(res); + } + #endregion + #region 获取用户已订阅套餐 + [HttpGet] + [Authorize(Roles = "User")] + public async Task>>> GetUserPackageList(int pageIndex,int pageSize,bool desc) + { + var selfUserId = User.Claims.First(x => x.ValueType == "userId").Value; + var list = await packageService.GetUserPackagesAsync(int.Parse(selfUserId)); + var res = new ResponseBase>(list); + return Ok(res); + } + #endregion + #region 管理员获取用户已订阅套餐 + [HttpGet] + [Authorize(Roles = "Admin")] + public async Task>>> GetUserPackageListAdmin(int userId,int pageIndex, int pageSize, bool desc) + { + var list = await packageService.GetUserPackagesAsync(userId); + var res = new ResponseBase>(list); + return Ok(res); + } + #endregion + #region 添加套餐有效期 + [HttpPost] + [Authorize(Roles = "User")] + public async Task>> UserPackageUpdate(int packageId,int day) + { + TimeSpan timeSpan = TimeSpan.FromDays(day); + var selfUserId = User.Claims.First(x => x.ValueType == "userId").Value; + var userPackage = await packageService.AddUserPackageTimeAsync(packageId,int.Parse(selfUserId),timeSpan,false); + var res = new ResponseBase(userPackage); + return Ok(res); + } + #endregion + #region 管理员添加套餐有效期 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> UserPackageUpdateAdmin(int packageId,int userId, int day) + { + TimeSpan timeSpan = TimeSpan.FromDays(day); + var userPackage = await packageService.AddUserPackageTimeAsync(packageId, userId, timeSpan, true); + var res = new ResponseBase(userPackage); + return Ok(res); + } + #endregion + #region 扣减套餐有效期 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> DecuteUserPackage(int packageId,int userId,int day) + { + try + { + TimeSpan timeSpan = TimeSpan.FromDays(day); + var userPackage = await packageService.DecuteUserPackageTimeAsync(packageId, userId, timeSpan); + var res = new ResponseBase(userPackage); + 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/Controllers/SystemConfigController.cs b/Apimanager_backend/Controllers/SystemConfigController.cs new file mode 100644 index 0000000..0360fcb --- /dev/null +++ b/Apimanager_backend/Controllers/SystemConfigController.cs @@ -0,0 +1,64 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Models; +using Apimanager_backend.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Apimanager_backend.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + public class SystemConfigController : ControllerBase + { + private readonly ISystemConfigService systemConfigService; + private readonly ILogger logger; + public SystemConfigController(ISystemConfigService systemConfigService, ILogger logger) + { + this.systemConfigService = systemConfigService; + this.logger = logger; + } + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> UpdateSystemConfig([FromBody]UpdateSystemConfigDto updateSystemConfigDto) + { + try + { + var configInfo = await systemConfigService.UpdateSystemConfig(updateSystemConfigDto.ConfigName, updateSystemConfigDto.ConfigBody); + var res = new ResponseBase(configInfo); + return Ok(res); + }catch(BaseException e) + { + var res = new ResponseBase( + code:e.code, + message:e.message, + data:null + ); + return NotFound(res); + } + + } + [HttpGet] + [Authorize(Roles = "User")] + public async Task>> GetSystemConfig(string configName) + { + try + { + var configInfo = await systemConfigService.GetSystemConfig(configName); + var res = new ResponseBase(configInfo); + return Ok(res); + } + catch (BaseException e) + { + var res = new ResponseBase( + code: e.code, + message: e.message, + data: null + ); + return NotFound(res); + } + + } + } +} diff --git a/Apimanager_backend/Data/ApiContext.cs b/Apimanager_backend/Data/ApiContext.cs index f43a410..0bf0fa5 100644 --- a/Apimanager_backend/Data/ApiContext.cs +++ b/Apimanager_backend/Data/ApiContext.cs @@ -24,6 +24,8 @@ namespace Apimanager_backend.Data public DbSet UserRoles { get; set; } //日志表 public DbSet Logs { get; set; } + //系统配置表 + public DbSet SystemConfigs { get; set; } public ApiContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -31,6 +33,7 @@ namespace Apimanager_backend.Data // 配置全局查询筛选器 modelBuilder.Entity().HasQueryFilter(u => !u.IsDelete); modelBuilder.Entity().HasQueryFilter(a => !a.IsDelete); + modelBuilder.Entity().HasQueryFilter(x => x.IsDeleted); //配置日志表 modelBuilder.Entity().HasKey(x => x.Id); modelBuilder.Entity() diff --git a/Apimanager_backend/Data/SystemConfigConfig.cs b/Apimanager_backend/Data/SystemConfigConfig.cs new file mode 100644 index 0000000..ba83897 --- /dev/null +++ b/Apimanager_backend/Data/SystemConfigConfig.cs @@ -0,0 +1,31 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Newtonsoft.Json; + +namespace Apimanager_backend.Data +{ + public class SystemConfigConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + //主键 + builder.HasKey(x => x.Id); + //自增 + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + builder.HasData( + new SystemConfig(1,"SystemName", "青蓝"), + new SystemConfig(2,"SystemDescription", "描述"), + new SystemConfig(3,"LogoLocation", ""), + new SystemConfig(4,"FaviconLocation", ""), + new SystemConfig(5,"Phone","13000000000"), + new SystemConfig(6,"Email","admin@admin.com"), + new SystemConfig( + 7,"RegisterConfig", JsonConvert.SerializeObject(new {RegisterOn = true,Emailvalidate = true }) + ) + + ); + } + } +} diff --git a/Apimanager_backend/Data/UserConfig.cs b/Apimanager_backend/Data/UserConfig.cs index 7b5058a..6869388 100644 --- a/Apimanager_backend/Data/UserConfig.cs +++ b/Apimanager_backend/Data/UserConfig.cs @@ -18,6 +18,9 @@ namespace Apimanager_backend.Data .IsUnique(); builder.HasIndex(x => x.Email) .IsUnique(); + builder.HasData( + new User(-1,"admin", "admin1@admin.com","e10adc3949ba59abbe56e057f20f883e") + ); } } } diff --git a/Apimanager_backend/Dtos/UpdateSystemConfigDto.cs b/Apimanager_backend/Dtos/UpdateSystemConfigDto.cs new file mode 100644 index 0000000..027a9d1 --- /dev/null +++ b/Apimanager_backend/Dtos/UpdateSystemConfigDto.cs @@ -0,0 +1,8 @@ +namespace Apimanager_backend.Dtos +{ + public class UpdateSystemConfigDto + { + public string ConfigName { get; set; } + public string ConfigBody { get; set; } + } +} diff --git a/Apimanager_backend/Migrations/20241110134811_add-configTable.Designer.cs b/Apimanager_backend/Migrations/20241110134811_add-configTable.Designer.cs new file mode 100644 index 0000000..4a84112 --- /dev/null +++ b/Apimanager_backend/Migrations/20241110134811_add-configTable.Designer.cs @@ -0,0 +1,572 @@ +// +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("20241110134811_add-configTable")] + partial class addconfigTable + { + /// + 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("IsDeleted") + .HasColumnType("tinyint(1)"); + + 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.SystemConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ConfigBody") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ConfigName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SystemConfigs"); + + b.HasData( + new + { + Id = 1, + ConfigBody = "青蓝", + ConfigName = "SystemName" + }, + new + { + Id = 2, + ConfigBody = "描述", + ConfigName = "SystemDescription" + }, + new + { + Id = 3, + ConfigBody = "", + ConfigName = "LogoLocation" + }, + new + { + Id = 4, + ConfigBody = "", + ConfigName = "FaviconLocation" + }, + new + { + Id = 5, + ConfigBody = "13000000000", + ConfigName = "Phone" + }, + new + { + Id = 6, + ConfigBody = "admin@admin.com", + ConfigName = "Email" + }, + new + { + Id = 7, + ConfigBody = "{\"RegisterOn\":true,\"Emailvalidate\":true}", + ConfigName = "RegisterConfig" + }); + }); + + 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"); + + b.HasData( + new + { + Id = -1, + Balance = 0m, + CreatedAt = new DateTime(2024, 11, 10, 13, 48, 10, 873, DateTimeKind.Utc).AddTicks(7811), + Email = "admin1@admin.com", + IsBan = false, + IsDelete = false, + PassHash = "e10adc3949ba59abbe56e057f20f883e", + Username = "admin" + }); + }); + + modelBuilder.Entity("Apimanager_backend.Models.UserPackage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + 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/20241110134811_add-configTable.cs b/Apimanager_backend/Migrations/20241110134811_add-configTable.cs new file mode 100644 index 0000000..fff61d5 --- /dev/null +++ b/Apimanager_backend/Migrations/20241110134811_add-configTable.cs @@ -0,0 +1,99 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Apimanager_backend.Migrations +{ + /// + public partial class addconfigTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ExpiryDate", + table: "Apipackages"); + + migrationBuilder.AddColumn( + name: "ExpiryDate", + table: "UserPackages", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "Apipackages", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "SystemConfigs", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ConfigName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ConfigBody = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_SystemConfigs", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.InsertData( + table: "SystemConfigs", + columns: new[] { "Id", "ConfigBody", "ConfigName" }, + values: new object[,] + { + { 1, "青蓝", "SystemName" }, + { 2, "描述", "SystemDescription" }, + { 3, "", "LogoLocation" }, + { 4, "", "FaviconLocation" }, + { 5, "13000000000", "Phone" }, + { 6, "admin@admin.com", "Email" }, + { 7, "{\"RegisterOn\":true,\"Emailvalidate\":true}", "RegisterConfig" } + }); + + migrationBuilder.InsertData( + table: "Users", + columns: new[] { "Id", "ApiKey", "Balance", "CreatedAt", "Email", "IsBan", "IsDelete", "PassHash", "Username" }, + values: new object[] { -1, null, 0m, new DateTime(2024, 11, 10, 13, 48, 10, 873, DateTimeKind.Utc).AddTicks(7811), "admin1@admin.com", false, false, "e10adc3949ba59abbe56e057f20f883e", "admin" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SystemConfigs"); + + migrationBuilder.DeleteData( + table: "Users", + keyColumn: "Id", + keyValue: -1); + + migrationBuilder.DropColumn( + name: "ExpiryDate", + table: "UserPackages"); + + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "Apipackages"); + + migrationBuilder.AddColumn( + name: "ExpiryDate", + table: "Apipackages", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + } +} diff --git a/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs b/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs index bd0a686..4cce6c9 100644 --- a/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs +++ b/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs @@ -130,8 +130,8 @@ namespace Apimanager_backend.Migrations b.Property("CreatedAt") .HasColumnType("datetime(6)"); - b.Property("ExpiryDate") - .HasColumnType("datetime(6)"); + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() @@ -275,6 +275,69 @@ namespace Apimanager_backend.Migrations b.ToTable("Orders"); }); + modelBuilder.Entity("Apimanager_backend.Models.SystemConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ConfigBody") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ConfigName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SystemConfigs"); + + b.HasData( + new + { + Id = 1, + ConfigBody = "青蓝", + ConfigName = "SystemName" + }, + new + { + Id = 2, + ConfigBody = "描述", + ConfigName = "SystemDescription" + }, + new + { + Id = 3, + ConfigBody = "", + ConfigName = "LogoLocation" + }, + new + { + Id = 4, + ConfigBody = "", + ConfigName = "FaviconLocation" + }, + new + { + Id = 5, + ConfigBody = "13000000000", + ConfigName = "Phone" + }, + new + { + Id = 6, + ConfigBody = "admin@admin.com", + ConfigName = "Email" + }, + new + { + Id = 7, + ConfigBody = "{\"RegisterOn\":true,\"Emailvalidate\":true}", + ConfigName = "RegisterConfig" + }); + }); + modelBuilder.Entity("Apimanager_backend.Models.User", b => { b.Property("Id") @@ -318,6 +381,19 @@ namespace Apimanager_backend.Migrations .IsUnique(); b.ToTable("Users"); + + b.HasData( + new + { + Id = -1, + Balance = 0m, + CreatedAt = new DateTime(2024, 11, 10, 13, 48, 10, 873, DateTimeKind.Utc).AddTicks(7811), + Email = "admin1@admin.com", + IsBan = false, + IsDelete = false, + PassHash = "e10adc3949ba59abbe56e057f20f883e", + Username = "admin" + }); }); modelBuilder.Entity("Apimanager_backend.Models.UserPackage", b => @@ -326,6 +402,9 @@ namespace Apimanager_backend.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + b.Property("PackageId") .HasColumnType("int"); diff --git a/Apimanager_backend/Models/Apipackage.cs b/Apimanager_backend/Models/Apipackage.cs index 4e9eed3..2bac923 100644 --- a/Apimanager_backend/Models/Apipackage.cs +++ b/Apimanager_backend/Models/Apipackage.cs @@ -37,6 +37,10 @@ namespace Apimanager_backend.Models /// 创建时间 /// public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp + /// + /// 是否删除 + /// + public bool IsDeleted { get; set; } = false; //导航属性 public ICollection Apis { get; set; } diff --git a/Apimanager_backend/Models/SystemConfig.cs b/Apimanager_backend/Models/SystemConfig.cs new file mode 100644 index 0000000..001b330 --- /dev/null +++ b/Apimanager_backend/Models/SystemConfig.cs @@ -0,0 +1,16 @@ +namespace Apimanager_backend.Models +{ + public class SystemConfig + { + public int Id { get; set; } + public string ConfigName { get; set; } + public string ConfigBody { get; set; } + public SystemConfig(int id,string configName,string configBody) + { + this.Id = id; + this.ConfigName = configName; + this.ConfigBody = configBody; + } + public SystemConfig() { } + } +} diff --git a/Apimanager_backend/Models/User.cs b/Apimanager_backend/Models/User.cs index 1b4a180..5e71322 100644 --- a/Apimanager_backend/Models/User.cs +++ b/Apimanager_backend/Models/User.cs @@ -61,5 +61,13 @@ namespace Apimanager_backend.Models public ICollection CallLogs { get; set; } public ICollection Orders { get; set; } public ICollection Roles { get; set; } = new List(); + public User(int id,string username,string email,string passHash) + { + this.Id = id; + this.Username = username; + this.PassHash = passHash; + this.Email = email; + } + public User() { } } } diff --git a/Apimanager_backend/Program.cs b/Apimanager_backend/Program.cs index 6fee951..e0c8391 100644 --- a/Apimanager_backend/Program.cs +++ b/Apimanager_backend/Program.cs @@ -18,6 +18,7 @@ IConfiguration configuration = new ConfigurationBuilder() string? redStr = configuration["Redis:ConnectionString"]; string? constr = configuration.GetConnectionString("DefaultConnection"); //־ +/* Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() @@ -27,7 +28,7 @@ Log.Logger = new LoggerConfiguration() autoCreateTable:true ).CreateLogger(); builder.Host.UseSerilog(); - +*/ builder.Services.AddDbContext(option => option.UseMySql(constr, MySqlServerVersion.AutoDetect(constr)) ); @@ -61,6 +62,7 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); +app.UseCors("AllowAll"); app.UseAuthentication(); app.UseAuthorization(); diff --git a/Apimanager_backend/Services/AdminService.cs b/Apimanager_backend/Services/AdminService.cs index bfbcb43..03b76bd 100644 --- a/Apimanager_backend/Services/AdminService.cs +++ b/Apimanager_backend/Services/AdminService.cs @@ -67,7 +67,7 @@ namespace Apimanager_backend.Services #region 获取用户列表 public async Task> GetUsersAsync(int page, int pageSize, bool desc) { - var query = context.Users.Where(x => true) + var query = context.Users.Include(x => x.Roles).Where(x => true) .OrderBy(x => x.Id); //倒序 if (desc) diff --git a/Apimanager_backend/Services/IPackageService.cs b/Apimanager_backend/Services/IPackageService.cs index 1f883d6..f5beb16 100644 --- a/Apimanager_backend/Services/IPackageService.cs +++ b/Apimanager_backend/Services/IPackageService.cs @@ -1,4 +1,5 @@ using Apimanager_backend.Dtos; +using Apimanager_backend.Models; namespace Apimanager_backend.Services { @@ -37,6 +38,27 @@ namespace Apimanager_backend.Services /// /// public Task PackageInfoByIdAsync(int packageId); + /// + /// 获取用户所有订阅套餐 + /// + /// + /// + public Task> GetUserPackagesAsync(int userId); + /// + /// 增加套餐订阅时长 + /// + /// 套餐Id(注意这里不是用户订阅套餐表的Id) + /// + /// 判断是否为管理员操作(如果是则不扣除余额) + /// + public Task AddUserPackageTimeAsync(int packageId,int userId,TimeSpan time,bool isAdmin); + /// + /// 扣除套餐订阅时长 + /// + /// 套餐Id(注意这里不是用户订阅套餐表的Id) + /// + /// + public Task DecuteUserPackageTimeAsync(int packageId,int userId,TimeSpan time); } diff --git a/Apimanager_backend/Services/ISystemConfigService.cs b/Apimanager_backend/Services/ISystemConfigService.cs new file mode 100644 index 0000000..29e172c --- /dev/null +++ b/Apimanager_backend/Services/ISystemConfigService.cs @@ -0,0 +1,10 @@ +using Apimanager_backend.Models; + +namespace Apimanager_backend.Services +{ + public interface ISystemConfigService + { + public Task UpdateSystemConfig(string configName,string configBody); + public Task GetSystemConfig(string configName); + } +} diff --git a/Apimanager_backend/Services/PackageService.cs b/Apimanager_backend/Services/PackageService.cs new file mode 100644 index 0000000..06bd810 --- /dev/null +++ b/Apimanager_backend/Services/PackageService.cs @@ -0,0 +1,201 @@ +using Apimanager_backend.Data; +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Models; +using AutoMapper; +using AutoMapper.Configuration.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Apimanager_backend.Services +{ + public class PackageService : IPackageService + { + private readonly ApiContext apiContext; + private readonly ILogger logger; + private readonly IMapper mapper; + public PackageService(ApiContext apiContext,ILogger logger,IMapper mapper) + { + this.apiContext = apiContext; + this.logger = logger; + this.mapper = mapper; + } + #region 新增套餐 + public async Task AddPackageAsync(AddPackageDto addPackageDto) + { + var packageInfo = mapper.Map(addPackageDto); + apiContext.Apipackages.Add(packageInfo); + await apiContext.SaveChangesAsync(); + return mapper.Map(packageInfo); + } + #endregion + #region 增加用户订阅套餐 + public async Task AddUserPackageTimeAsync(int packageId,int userId,TimeSpan time,bool isAdmin) + { + await using var transaction = await apiContext.Database.BeginTransactionAsync(); + try + { + //查询用户名下有无对应套餐 + var userPackageInfo = await apiContext.UserPackages.FirstOrDefaultAsync( + x => x.PackageId == packageId && x.UserId == userId + ); + //查询套餐是否存在 + var packageInfo = await apiContext.Apipackages.FirstOrDefaultAsync(x => x.Id == packageId); + if (packageInfo == null) + { + throw new BaseException(4004,"套餐不存在"); + } + //查询用户是否存在 + var user = await apiContext.Users.FirstOrDefaultAsync(x => x.Id == userId); + if (user == null) + { + throw new BaseException(2004,"用户不存在"); + } + //如果没有对应套餐关联则新建 + if (userPackageInfo == null) + { + userPackageInfo = new UserPackage + { + PackageId = packageId, + UserId = userId, + ExpiryDate = DateTime.UtcNow + time, + }; + apiContext.UserPackages.Add(userPackageInfo); + } + else + { + //否则直接在关联记录上添加有效期 + userPackageInfo.ExpiryDate += time; + apiContext.UserPackages.Update(userPackageInfo); + } + if (!isAdmin) + { + //扣除对应用户余额 + user.Balance -= packageInfo.Price * time.Days; + apiContext.Users.Update(user); + } + await apiContext.SaveChangesAsync(); + await transaction.CommitAsync(); + return userPackageInfo; + } catch (Exception) + { + await transaction.RollbackAsync(); + throw; + } + + } + #endregion + #region 扣除用户订阅套餐 + + public async Task DecuteUserPackageTimeAsync(int packageId,int userId, TimeSpan time) + { + await using var transaction = await apiContext.Database.BeginTransactionAsync(); + try + { + //查询用户名下有无对应套餐 + var userPackageInfo = await apiContext.UserPackages.FirstOrDefaultAsync( + x => x.PackageId == packageId && x.UserId == userId + ); + //如果没有对应套餐则返回错误信息 + if (userPackageInfo == null) + { + throw new BaseException(4004,"套餐未找到"); + } + //否则直接在关联记录上添加有效期 + userPackageInfo.ExpiryDate -= time; + apiContext.UserPackages.Update(userPackageInfo); + await apiContext.SaveChangesAsync(); + await transaction.CommitAsync(); + return userPackageInfo; + } + catch (Exception) + { + await transaction.RollbackAsync(); + throw; + } + } + #endregion + #region 删除套餐 + public async Task DeletePackageAsync(int packageId) + { + await using var transaction = await apiContext.Database.BeginTransactionAsync(); + try + { + var packageInfo = await apiContext.Apipackages.FirstOrDefaultAsync(x => x.Id == packageId); + if (packageInfo == null) + { + throw new BaseException(4004, "套餐未找到"); + } + packageInfo.IsDeleted = true; + apiContext.Apipackages.Update(packageInfo); + await apiContext.SaveChangesAsync(); + await transaction.CommitAsync(); + }catch(Exception) + { + transaction.Rollback(); + throw; + } + } + #endregion + #region 获取套餐列表 + public async Task> GetAllPackagesAsync(int pageIndex, int pageSize, bool desc) + { + IQueryable query = apiContext.Apipackages.Where(x => true).OrderBy(x => x.Id); + if(desc) + { + query = query.OrderDescending(); + } + query = query.Skip((pageIndex - 1) * pageSize).Take(pageSize); + var list = await query.ToListAsync(); + return mapper.Map>(list); + } + #endregion + #region 获取用户订阅套餐列表 + public async Task> GetUserPackagesAsync(int userId) + { + var userpackageInfo = await apiContext.UserPackages.Where(x => x.UserId == userId).ToListAsync(); + return userpackageInfo; + } + #endregion + #region 获取套餐信息 + public async Task PackageInfoByIdAsync(int packageId) + { + var packageInfo = await apiContext.Apipackages.FirstOrDefaultAsync(x => x.Id == packageId); + if(packageInfo == null) + { + throw new BaseException(4004,"套餐未找到"); + } + return mapper.Map(packageInfo); + } + #endregion + #region 更新套餐信息 + public async Task UpdatePackageAsync(int packageId, UpdatePackageDto updatePackageDto) + { + await using var transaction = await apiContext.Database.BeginTransactionAsync(); + try + { + //查询对应套餐 + var packageInfo = await apiContext.Apipackages.FirstOrDefaultAsync(x => x.Id == packageId); + if (packageInfo == null) + { + throw new BaseException(4004, "Success"); + } + //更新字段 + packageInfo.Name = updatePackageDto.Name ?? packageInfo.Name; + packageInfo.CallLimit = updatePackageDto.CallLimit != null ? updatePackageDto.CallLimit.Value : packageInfo.CallLimit; + packageInfo.OneMinuteLimit = updatePackageDto.OneMinuteLimit != null ? updatePackageDto.OneMinuteLimit.Value : packageInfo.OneMinuteLimit; + packageInfo.Price = updatePackageDto.Price != null ? updatePackageDto.Price.Value : packageInfo.Price; + //更新 + apiContext.Apipackages.Update(packageInfo); + await apiContext.SaveChangesAsync(); + await transaction.CommitAsync(); + return mapper.Map(packageInfo); + } + catch (Exception) + { + await transaction.RollbackAsync(); + throw; + } + } + #endregion + } +} diff --git a/Apimanager_backend/Services/SystemConfigService.cs b/Apimanager_backend/Services/SystemConfigService.cs new file mode 100644 index 0000000..6b5bd49 --- /dev/null +++ b/Apimanager_backend/Services/SystemConfigService.cs @@ -0,0 +1,40 @@ + +using Apimanager_backend.Data; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; + +namespace Apimanager_backend.Services +{ + public class SystemConfigService : ISystemConfigService + { + private readonly ApiContext apiContext; + private readonly ILogger logger; + public SystemConfigService(ApiContext apiContext,ILogger logger) + { + this.apiContext = apiContext; + this.logger = logger; + } + public async Task UpdateSystemConfig(string configName, string configBody) + { + var config = await apiContext.SystemConfigs.FirstOrDefaultAsync(x => x.ConfigName == configName); + if (config == null) + { + throw new BaseException(5006,"配置不存在"); + } + config.ConfigBody = configBody; + apiContext.SystemConfigs.Update(config); + await apiContext.SaveChangesAsync(); + return config; + } + public async Task GetSystemConfig(string configName) + { + var config = await apiContext.SystemConfigs.FirstOrDefaultAsync(x => x.ConfigName == configName); + if (config == null) + { + throw new BaseException(5006, "配置不存在"); + } + return config; + } + } +} diff --git a/Apimanager_backend/Services/UserService.cs b/Apimanager_backend/Services/UserService.cs index 76528c1..95a583f 100644 --- a/Apimanager_backend/Services/UserService.cs +++ b/Apimanager_backend/Services/UserService.cs @@ -31,7 +31,7 @@ namespace Apimanager_backend.Services } public async Task GetUserAsync(int userId) { - User? user = await apiContext.Users.SingleOrDefaultAsync(x => x.Id == userId); + User? user = await apiContext.Users.Include(x => x.Roles).SingleOrDefaultAsync(x => x.Id == userId); //未找到用户 if (user == null) { diff --git a/Apimanager_backend/appsettings.json b/Apimanager_backend/appsettings.json index eb6d675..3f75902 100644 --- a/Apimanager_backend/appsettings.json +++ b/Apimanager_backend/appsettings.json @@ -7,7 +7,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "DefaultConnection": "server=192.168.5.200;username=root;password=768788Dyw@;port=3306;database=api_billing_system;SslMode=Preferred;" + "DefaultConnection": "server=192.168.5.100;username=root;password=768788dyw;port=3306;database=api_billing_system;SslMode=Preferred;" }, "JwtSettings": { "Secret": "deXtdXode6hv0SI1o6xRw1ALkn0vYsWn", @@ -17,7 +17,7 @@ "RefreshTokenExpiryDays": 7 }, "redis": { - "ConnectionString": "192.168.5.200:6379" + "ConnectionString": "192.168.5.100:6379" }, "EmailSettings": { "Server": "smtp.exmail.qq.com", diff --git a/ErrorCode.md b/ErrorCode.md index 4b832d6..38488a3 100644 --- a/ErrorCode.md +++ b/ErrorCode.md @@ -46,9 +46,10 @@ | 4001 | 400 | 支付请求无效 | Invalid payment request | | 4002 | 402 | 支付失败,余额不足 | Payment failed, insufficient balance | | 4003 | 404 | 订单未找到 | Order not found | -| 4004 | 409 | 重复支付或订单冲突 | Duplicate payment or order conflict | -| 4005 | 500 | 支付系统错误 | Payment system error | -| 4006 | 200 | 退款成功 | Refund successful | +| 4004 | 404 | 套餐未找到 | Package not found | +| 4005 | 409 | 重复支付或订单冲突 | Duplicate payment or order conflict | +| 4006 | 500 | 支付系统错误 | Payment system error | +| 4007 | 200 | 退款成功 | Refund successful | #### 日志与系统模块错误码(5xxx) @@ -59,4 +60,5 @@ | 5002 | 500 | 日志服务异常 | Log service error | | 5003 | 403 | 无权限查看操作日志 | No permission to view logs | | 5004 | 500 | 邮件发送错误 | Email send error | -| 5005 | 400 | 验证码错误 | ValidateCode Error | \ No newline at end of file +| 5005 | 400 | 验证码错误 | ValidateCode Error | +| 5006 | 404 | 配置不存在 | Config not found | \ No newline at end of file