diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/APIDefine.md b/APIDefine.md new file mode 100644 index 0000000..98140ff --- /dev/null +++ b/APIDefine.md @@ -0,0 +1,37 @@ +## API路径及方法定义 + +| 模块 | 接口名称 | 路径 | 方法 | 描述 | +| -------- | ---------------- | ------------------------------------------------------------ | ------ | ---------------- | +| 用户 | 登录 | /api/user/login | POST | 用户登录 | +| | 注册 | /api/user/register | POST | 用户注册 | +| | 登出 | /api/user/logout | POST | 用户登出 | +| | 重置密码 | /api/user/resetpassword | POST | 找回密码 | +| | 用户信息 | /api/user/info/{id} | GET | 获取用户信息 | +| | 修改用户信息 | /api/user/update | POST | 修改用户信息 | +| | 用户充值 | /api/user/recharge | POST | 用户充值 | +| | 添加用户 | /api/admin/adduser | POST | 管理员添加用户 | +| | 删除用户 | /api/admin/deleteuser/{id} | DELETE | 管理员删除用户 | +| | 添加余额 | /api/admin/updatebalace | POST | 管理员修改余额 | +| | 禁用用户 | /api/admin/ban/{id} | POST | 禁用用户 | +| | 用户列表 | /api/admin/userlist?page={index}&pagesize={size}&desc={true\|false} | GET | 查询用户列表 | +| API | 添加接口 | /api/admin/addapi | POST | 添加API | +| | 删除接口 | /api/admin/deleteapi/{id} | DELETE | 删除API | +| | 禁用接口 | /api/admin/banapi/{id} | POST | 禁用API | +| | 修改接口 | /api/admin/updateapi?id={id} | POST | 修改接口信息 | +| | 接口信息 | /api/apiinfo/{id} | GET | 获取API信息 | +| | 接口列表 | /api/admin/apilist?page={index}&pagesize={size}&desc={true\|false} | GET | 查询接口列表 | +| | 接口调用查询 | /api/apilog?userid={id}&starttime={time}&endtime={time}&method={method}&page={index}&apiid={id}&userid={id}&pagesize={size}&desc={true\|false} | GET | 查询接口调用 | +| 套餐 | 添加套餐 | /api/admin/addpackage | POST | 添加套餐 | +| | 修改套餐 | /api/admin/updatepackage?id={id} | POST | 修改套餐信息 | +| | 删除套餐 | /api/admin/deletepackage/{id} | DELETE | 删除套餐 | +| | 套餐列表 | /api/admin/packagelist?page={index}&pagesize={size} | GET | 套餐列表 | +| | 购买套餐 | /api/user/buypackage/?packageid={id} | POST | 购买套餐 | +| 订单 | 支付记录查询 | /api/payment/loglist?payid={id}&userid={id}&starttime={time}&endtime={time}&method={method}&page={index}&pagesize={size}&desc={true\|false} | GET | 支付记录查询 | +| | 订单信息查询 | /api/payment/orderlog/{id} | POST | 订单查询 | +| | 取消订单 | /api/payment/cancelorder/{id} | DELETE | 取消订单 | +| 用户套餐 | 添加用户套餐 | /api/admin/userpackageadd | POST | 添加用户关联套餐 | +| | 修改用户套餐时间 | /api/admin/userpackageupdate/id={id}&expire={time} | DELETE | 删除用户关联套餐 | +| | 用户套餐查询 | /api/admin/userpackagelist/{id} | GET | 用户已购套餐查询 | +| 系统配置 | 配置修改 | /api/admin/updateconfig | POST | 系统配置修改 | +| 操作日志 | 操作日志列表 | /api/operation/log?userid={id}&page={index}&pagesize={size}&starttime={time}&endtime={time}&desc={true\|false} | GET | 操作日志查询 | + diff --git a/Apimanager_backend.sln b/Apimanager_backend.sln new file mode 100644 index 0000000..36300f7 --- /dev/null +++ b/Apimanager_backend.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35312.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apimanager_backend", "Apimanager_backend\Apimanager_backend.csproj", "{F4FD5145-6971-4032-B2B6-D7A61AF8BB2E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F4FD5145-6971-4032-B2B6-D7A61AF8BB2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4FD5145-6971-4032-B2B6-D7A61AF8BB2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4FD5145-6971-4032-B2B6-D7A61AF8BB2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4FD5145-6971-4032-B2B6-D7A61AF8BB2E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1E84B967-07AF-4318-9BC3-8C66563CE795} + EndGlobalSection +EndGlobal diff --git a/Apimanager_backend/Apimanager_backend.csproj b/Apimanager_backend/Apimanager_backend.csproj new file mode 100644 index 0000000..059696b --- /dev/null +++ b/Apimanager_backend/Apimanager_backend.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/Apimanager_backend/Apimanager_backend.http b/Apimanager_backend/Apimanager_backend.http new file mode 100644 index 0000000..1122844 --- /dev/null +++ b/Apimanager_backend/Apimanager_backend.http @@ -0,0 +1,6 @@ +@Apimanager_backend_HostAddress = http://localhost:5292 + +GET {{Apimanager_backend_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Apimanager_backend/Config/MyAutomapper.cs b/Apimanager_backend/Config/MyAutomapper.cs new file mode 100644 index 0000000..f0895eb --- /dev/null +++ b/Apimanager_backend/Config/MyAutomapper.cs @@ -0,0 +1,20 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Models; +using AutoMapper; + +namespace Apimanager_backend.Config +{ + public class MyAutomapper:Profile + { + public MyAutomapper() + { + CreateMap(); + CreateMap() + .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 new file mode 100644 index 0000000..8f0a524 --- /dev/null +++ b/Apimanager_backend/Config/ServiceCollectionExtensions.cs @@ -0,0 +1,104 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Services; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json; +using StackExchange.Redis; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Apimanager_backend.Config +{ + public static class ServiceCollectionExtensions + { + 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(); + return services; + } + public static IServiceCollection AddJWTService(this IServiceCollection services,IConfiguration configuration) + { + var jwtSettings = configuration.GetSection("JwtSettings"); + var key = Encoding.ASCII.GetBytes(jwtSettings["Secret"]); + // JWT配置 + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + //jwt参数 + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = jwtSettings["Issuer"], + ValidAudience = jwtSettings["Audience"], + IssuerSigningKey = new SymmetricSecurityKey(key) + }; + //添加自定义响应处理函数 + options.Events = new JwtBearerEvents + { + OnChallenge = new Func(JwtTokenErrorEventFunc), + OnForbidden = new Func(JwtPermissionEventFunc) + }; + }); + + //redis配置 + services.AddSingleton(ConnectionMultiplexer.Connect(configuration["Redis:ConnectionString"])); + return services; + } + /// + /// token无效事件处理函数 + /// + /// + /// + public async static Task JwtTokenErrorEventFunc(JwtBearerChallengeContext context) + { + context.Response.ContentType = "application/json"; + var res = new ResponseBase( + code: 1002, + message: "用户未登录或认证失败", + data: null + ); + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + await context.Response.WriteAsync(JsonConvert.SerializeObject(res)); + context.HandleResponse(); + } + public async static Task JwtPermissionEventFunc(ForbiddenContext context) + { + context.Response.ContentType = "application/json"; + var res = new ResponseBase( + code: 2006, + message: "用户无权限进行该操作", + data: null + ); + 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/AdminController.cs b/Apimanager_backend/Controllers/AdminController.cs new file mode 100644 index 0000000..0a752c4 --- /dev/null +++ b/Apimanager_backend/Controllers/AdminController.cs @@ -0,0 +1,149 @@ +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 AdminController : ControllerBase + { + private readonly IAdminService adminService; + private readonly IUserService userService; + public AdminController(IAdminService service,IUserService userService) + { + this.adminService = service; + this.userService = userService; + } + #region 获取用户列表 + [HttpGet] + [Authorize(Roles = "Admin")] + public async Task>>> UserList(int pageIndex,int pageSize,bool desc) + { + var users = await adminService.GetUsersAsync(pageIndex,pageSize,desc); + var res = new ResponseBase>( + code:1000, + message:"Success", + data:users + ); + return Ok(res); + } + #endregion + #region 获取用户信息 + [HttpGet] + [Authorize(Roles = "Admin")] + public async Task>> UserInfo(int userId) + { + var userInfo = await userService.GetUserAsync(userId); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: userInfo + ); + return Ok(res); + } + #endregion + #region 删除用户 + [HttpDelete] + [Authorize(Roles = "Admin")] + public async Task>> DeleteUser(int userId) + { + await adminService.DeleteUserAsync(userId); + var res = new ResponseBase( + code:1000, + message:"Success", + data: null + ); + return Ok(res); + } + #endregion + #region 添加用户 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> AddUser([FromBody]CreateUserDto dto) + { + var userInfo = await adminService.CreateUserAsync(dto); + var res = new ResponseBase( + code:1000, + message:"Success", + data: userInfo + ); + return Ok(res); + } + #endregion + #region 禁用用户 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> Ban(int userId) + { + await adminService.BanUserAsync(userId); + var res = new ResponseBase( + code:1000, + message:"Success", + data: null + ); + return Ok(res); + } + #endregion + #region 取消禁用用户 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> UnBan(int userId) + { + await adminService.UnbanUserAsync(userId); + var res = new ResponseBase( + code:1000, + message:"Success", + data:null + ); + return Ok(res); + } + #endregion + #region 更新用户信息 + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> UpdateUser([FromQuery]int userId,[FromBody]AdminUpdateUserDto dto) + { + try + { + var userInfo = await adminService.UpdateUserAsync(userId, dto); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: userInfo + ); + 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 = "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/Controllers/AuthController.cs b/Apimanager_backend/Controllers/AuthController.cs new file mode 100644 index 0000000..cbbca15 --- /dev/null +++ b/Apimanager_backend/Controllers/AuthController.cs @@ -0,0 +1,172 @@ +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; +using Microsoft.VisualBasic; + +namespace Apimanager_backend.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + public class AuthController : ControllerBase + { + private readonly IAuthService authService; + private readonly ITokenService tokenService; + private readonly IRefreshTokenService refreshTokenService; + private readonly IUserService userService; + public AuthController(IAuthService authService, ITokenService tokenService, IRefreshTokenService refreshTokenService,IUserService userService) + { + this.authService = authService; + this.tokenService = tokenService; + this.refreshTokenService = refreshTokenService; + this.userService = userService; + } + /// + /// 用户登录控制器 + /// + /// 登录信息 + /// 通用返回信息格式 + [HttpPost] + public async Task>> Login([FromBody] UserLoginDto dto) + { + UserInfoDto user = await authService.LoginAsync(dto.UserName, dto.Password); + //生成token + string token = tokenService.GenerateAccessToken(user.Id.ToString(), user.Roles); + //生成refreshtoken + string refreshToken = await refreshTokenService.CreateRefereshTokenAsync(user.Id.ToString()); + var responseInfo = new ResponseBase( + code: 2000, + message: "Login successful", + data: new LoginResponseDto + { + UserInfo = user, + Token = token, + RefreshToken = refreshToken + } + ); + return Ok(responseInfo); + + } + /// + /// 令牌刷新 + /// + /// 传入用户令牌 + /// 返回新令牌 + [HttpPost] + public async Task>> Refresh([FromBody]RefreshResponseDto dto) + { + var IsRefreshToken = await refreshTokenService.ValidateRefreshTokenAsync(dto.UserId.ToString(),dto.RefreshToken); + //刷新令牌无效 + if (!IsRefreshToken) + { + var ret = new ResponseBase( + code: 2008, + message: "Refresh expires or is invalid", + data: null + ); + return Unauthorized(ret); + } + //获取刷新令牌对应用户信息 + var userInfo = await userService.GetUserAsync(dto.UserId); + //重新生成令牌 + var token = tokenService.GenerateAccessToken(userInfo.Id.ToString(), userInfo.Roles); + //刷新刷新令牌有效期(小于三天才会刷新) + await refreshTokenService.UpdateRefreshTokenAsync(userInfo.Id.ToString()); + var result = new ResponseBase( + code: 1000, + message: "Success", + data: new RefreshResponseDto + { + UserId = dto.UserId, + Token = token, + RefreshToken = dto.RefreshToken + } + + ); + return Ok(result); + } + /// + /// 用户注册 + /// + /// + /// + [HttpPost] + public async Task>> Register(RegisterRequestDto requestDto) + { + var isUsernameExist = await userService.IsUsernameExist(requestDto.Username); + if (isUsernameExist) + { + var errorRes = new ResponseBase( + code: 2003, + message: "用户名已存在", + data: null + ); + return StatusCode(409, errorRes); + } + try + { + var userInfo = await authService.RegisterAsync(requestDto); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: userInfo + ); + return Ok(res); + } + catch (BaseException e) + { + var res = new ResponseBase( + code: e.code, + message: e.message, + data: null + ); + return StatusCode(500, res); + } + } + /// + /// 发送邮箱校验码 + /// + /// + /// + [HttpPost] + public async Task>> SendValidateCode([FromQuery]string email) + { + //检测邮箱是否被使用 + var emailIsUse = await userService.IsEmailExist(email); + if (emailIsUse) + { + var errorRes = new ResponseBase( + code:2005, + message: "邮箱已存在", + data:null + ); + return StatusCode(409,errorRes); + } + //发送注册验证码 + await authService.SendRegisterCodeAsync(email); + var res = new ResponseBase( + code:1000, + message:"Success", + data: null + ); + return Ok(res); + } + + [HttpDelete] + [Authorize(Roles = "User")] + public async Task>> Logout() + { + var userId = User.Claims.First(x => x.ValueType == "userId").Value; + await refreshTokenService.DeleterRefreshTokenAsync(userId); + var res = new ResponseBase( + code:1000, + message:"Success", + data: null + ); + return Ok(res); + } + } +} 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/Controllers/UserController.cs b/Apimanager_backend/Controllers/UserController.cs new file mode 100644 index 0000000..d6ce368 --- /dev/null +++ b/Apimanager_backend/Controllers/UserController.cs @@ -0,0 +1,91 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Apimanager_backend.Filters; +using Microsoft.AspNetCore.Authorization; +using Apimanager_backend.Models; +using System.Security.Claims; + +namespace Apimanager_backend.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + public class UserController : ControllerBase + { + private readonly IUserService userService; + public UserController(IUserService userService) + { + this.userService = userService; + } + /// + /// 获取用户个人信息 + /// + /// + [HttpGet] + [Authorize(Roles = "User")] + public async Task>> UserInfo() + { + var userId = User.Claims.First(x => x.Type == "userId").Value; + var userInfo = await userService.GetUserAsync(int.Parse(userId)); + var res = new ResponseBase( + code:1000, + message:"Success", + data:userInfo + ); + return Ok(res); + } + /// + /// 重置用户密码 + /// + /// + /// + [HttpPost] + public async Task>> Resetpassword([FromBody]ResetPasswordDto dto) + { + try + { + await userService.ResetPasswordAsync(dto.Email, dto.Code, dto.NewPassword); + 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 StatusCode(400,res); + } + } + [HttpPost] + public async Task>> SendResetEmail([FromQuery]string email) + { + await userService.SendResetPasswordEmailAsync(email); + var res = new ResponseBase( + code: 1000, + message: "Success", + data: null + ); + return Ok(res); + } + [HttpPost] + [Authorize(Roles = "User")] + public async Task>> Update([FromBody]UpdateUserDto dto) + { + var userId = User.Claims.First(x => x.ValueType == "userId").Value; + var userInfo = await userService.UpdateUserAsync(int.Parse(userId),dto); + var res = new ResponseBase( + code:1000, + message:"Success", + data:userInfo + ); + return Ok(res); + } + } +} diff --git a/Apimanager_backend/Data/ApiCallLogConfig.cs b/Apimanager_backend/Data/ApiCallLogConfig.cs new file mode 100644 index 0000000..b4484b0 --- /dev/null +++ b/Apimanager_backend/Data/ApiCallLogConfig.cs @@ -0,0 +1,26 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Apimanager_backend.Data +{ + public class ApiCallLogConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + //主键 + builder.HasKey(x => x.Id); + //自增 + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + //外键:API + builder.HasOne(x => x.Api) + .WithMany(u => u.ApiCalls) + .HasForeignKey(x => x.ApiId); + //外键:User + builder.HasOne(x => x.User) + .WithMany(u => u.CallLogs) + .HasForeignKey(x => x.UserId); + } + } +} diff --git a/Apimanager_backend/Data/ApiConfig.cs b/Apimanager_backend/Data/ApiConfig.cs new file mode 100644 index 0000000..cf36445 --- /dev/null +++ b/Apimanager_backend/Data/ApiConfig.cs @@ -0,0 +1,23 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Apimanager_backend.Data +{ + public class ApiConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + //主键 + builder.HasKey(x => x.Id); + //自增 + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + //外键 + builder.HasOne(x => x.Package) + .WithMany(u => u.Apis) + .HasForeignKey(x => x.PackageId); + + } + } +} diff --git a/Apimanager_backend/Data/ApiContext.cs b/Apimanager_backend/Data/ApiContext.cs new file mode 100644 index 0000000..0bf0fa5 --- /dev/null +++ b/Apimanager_backend/Data/ApiContext.cs @@ -0,0 +1,45 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using System.Reflection; + +namespace Apimanager_backend.Data +{ + public class ApiContext:DbContext + { + //API表 + public DbSet Apis { get; set; } + //用户表 + public DbSet Users { get; set; } + //API调用日志 + public DbSet CallLogs { get; set; } + //套餐表 + public DbSet Apipackages { get; set; } + //操作日志表 + public DbSet OperationLogs { get; set; } + //订单表 + public DbSet Orders { get; set; } + //用户已订购套餐表 + public DbSet UserPackages { get; set; } + //用户角色表 + 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) + { + modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApiContext).Assembly); + // 配置全局查询筛选器 + 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() + .Property(x => x.Id) + .ValueGeneratedOnAdd(); + } + + } +} 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/Data/OperationLogConfig.cs b/Apimanager_backend/Data/OperationLogConfig.cs new file mode 100644 index 0000000..01d83ae --- /dev/null +++ b/Apimanager_backend/Data/OperationLogConfig.cs @@ -0,0 +1,22 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Apimanager_backend.Data +{ + public class OperationLogConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + //主键 + builder.HasKey(x => x.Id); + //自增 + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + //外键 + builder.HasOne(x => x.User) + .WithMany(u => u.operationLogs) + .HasForeignKey(x => x.UserId); + } + } +} diff --git a/Apimanager_backend/Data/OrderConfig.cs b/Apimanager_backend/Data/OrderConfig.cs new file mode 100644 index 0000000..50d6452 --- /dev/null +++ b/Apimanager_backend/Data/OrderConfig.cs @@ -0,0 +1,28 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Apimanager_backend.Data +{ + public class OrderConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + //主键 + builder.HasKey(x => x.Id); + //自增 + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + //唯一索引 + builder.HasIndex(x => x.OrderNumber) + .IsUnique(); + //唯一索引 + builder.HasIndex(x => x.ThirdPartyOrderId) + .IsUnique(); + //外键 + builder.HasOne(x => x.User) + .WithMany(u => u.Orders) + .HasForeignKey(x => x.UserId); + } + } +} 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 new file mode 100644 index 0000000..6869388 --- /dev/null +++ b/Apimanager_backend/Data/UserConfig.cs @@ -0,0 +1,26 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Apimanager_backend.Data +{ + public class UserConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + //主键 + builder.HasKey(x => x.Id); + //自增 + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + //唯一索引 + builder.HasIndex(x => x.Username) + .IsUnique(); + builder.HasIndex(x => x.Email) + .IsUnique(); + builder.HasData( + new User(-1,"admin", "admin1@admin.com","e10adc3949ba59abbe56e057f20f883e") + ); + } + } +} diff --git a/Apimanager_backend/Data/UserPackageConfig.cs b/Apimanager_backend/Data/UserPackageConfig.cs new file mode 100644 index 0000000..032d923 --- /dev/null +++ b/Apimanager_backend/Data/UserPackageConfig.cs @@ -0,0 +1,25 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Apimanager_backend.Data +{ + public class UserPackageConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + //主键 + builder.HasKey(x => x.Id); + //自增 + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + //外键 + builder.HasOne(x => x.User) + .WithMany(u => u.Packages) + .HasForeignKey(x => x.UserId); + builder.HasOne(x => x.Package) + .WithMany(u => u.Packages) + .HasForeignKey(x => x.PackageId); + } + } +} diff --git a/Apimanager_backend/Data/UserRoleConfig.cs b/Apimanager_backend/Data/UserRoleConfig.cs new file mode 100644 index 0000000..401058d --- /dev/null +++ b/Apimanager_backend/Data/UserRoleConfig.cs @@ -0,0 +1,21 @@ +using Apimanager_backend.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Apimanager_backend.Data +{ + public class UserRoleConfig : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + //自增 + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + //外键 + builder.HasOne(x => x.User) + .WithMany(u => u.Roles) + .HasForeignKey(x => x.UserId); + } + } +} 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 new file mode 100644 index 0000000..f084d72 --- /dev/null +++ b/Apimanager_backend/Dtos/AdminUpdateUserDto.cs @@ -0,0 +1,8 @@ +namespace Apimanager_backend.Dtos +{ + public class AdminUpdateUserDto + { + 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/Base/ResponseBase.cs b/Apimanager_backend/Dtos/Base/ResponseBase.cs new file mode 100644 index 0000000..daa2563 --- /dev/null +++ b/Apimanager_backend/Dtos/Base/ResponseBase.cs @@ -0,0 +1,26 @@ +namespace Apimanager_backend.Dtos +{ + /// + /// 响应基类,构造最后的返回结果 + /// + /// 返回的具体数据类型 + public class ResponseBase + { + public int Code { get; set; } + public string Message { get; set; } + public T? Data { get; set; } + public ResponseBase(int code,string message,T data) + { + this.Code = code; + 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/CreateUserDto.cs b/Apimanager_backend/Dtos/CreateUserDto.cs new file mode 100644 index 0000000..95aae53 --- /dev/null +++ b/Apimanager_backend/Dtos/CreateUserDto.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Dtos +{ + public class CreateUserDto + { + [Required(ErrorMessage = "用户名必填")] + [MaxLength(20,ErrorMessage = "用户名最大长度20字符")] + public string Username { get; set; } + [Required(ErrorMessage = "密码必填")] + public string Password { get; set; } + [EmailAddress(ErrorMessage = "邮箱格式错误")] + public string Email { get; set; } + + } +} diff --git a/Apimanager_backend/Dtos/LoginResponseDto.cs b/Apimanager_backend/Dtos/LoginResponseDto.cs new file mode 100644 index 0000000..0dd5e0d --- /dev/null +++ b/Apimanager_backend/Dtos/LoginResponseDto.cs @@ -0,0 +1,11 @@ +using Apimanager_backend.Models; + +namespace Apimanager_backend.Dtos +{ + public class LoginResponseDto + { + public UserInfoDto UserInfo { get; set; } + public string Token { get; set; } + public string RefreshToken { 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/RefreshResponseDto.cs b/Apimanager_backend/Dtos/RefreshResponseDto.cs new file mode 100644 index 0000000..642b43c --- /dev/null +++ b/Apimanager_backend/Dtos/RefreshResponseDto.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Dtos +{ + public class RefreshResponseDto + { + [Required(ErrorMessage = "用户ID必填!")] + public int UserId { get; set; } + public string? Token { get; set; } + [Required(ErrorMessage = "刷新令牌必填!")] + public string RefreshToken { get; set; } + } +} diff --git a/Apimanager_backend/Dtos/RegisterRequestDto.cs b/Apimanager_backend/Dtos/RegisterRequestDto.cs new file mode 100644 index 0000000..192b02f --- /dev/null +++ b/Apimanager_backend/Dtos/RegisterRequestDto.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Dtos +{ + public class RegisterRequestDto + { + [Required(ErrorMessage = "用户名必填!")] + [MaxLength(20,ErrorMessage = "用户名最长20字符!")] + public string Username { get; set; } + [Required(ErrorMessage = "密码必填!")] + public string Password { get; set; } + [Required(ErrorMessage = "邮箱必填!")] + public string Email { get; set; } + [Required(ErrorMessage = "验证码必填!")] + public string VerificationCode { get; set; } + } +} diff --git a/Apimanager_backend/Dtos/ResetPasswordDto.cs b/Apimanager_backend/Dtos/ResetPasswordDto.cs new file mode 100644 index 0000000..7b4d394 --- /dev/null +++ b/Apimanager_backend/Dtos/ResetPasswordDto.cs @@ -0,0 +1,9 @@ +namespace Apimanager_backend.Dtos +{ + public class ResetPasswordDto + { + public string Email { get; set; } + public string NewPassword { get; set; } + public string Code { 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/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/Dtos/UpdateUserDto.cs b/Apimanager_backend/Dtos/UpdateUserDto.cs new file mode 100644 index 0000000..7f3411d --- /dev/null +++ b/Apimanager_backend/Dtos/UpdateUserDto.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace Apimanager_backend.Dtos +{ + public class UpdateUserDto + { + public string? password { get; set; } + } +} diff --git a/Apimanager_backend/Dtos/UserInfoBaseDto.cs b/Apimanager_backend/Dtos/UserInfoBaseDto.cs new file mode 100644 index 0000000..485c1b2 --- /dev/null +++ b/Apimanager_backend/Dtos/UserInfoBaseDto.cs @@ -0,0 +1,16 @@ +using Apimanager_backend.Models; + +namespace Apimanager_backend.Dtos +{ + public class UserInfoBaseDto + { + public UserInfoDto UserInfo { get; set; } + public List Roles { get; set; } + public UserInfoBaseDto(UserInfoDto userInfo, List roles) + { + UserInfo = userInfo; + Roles = roles; + } + public UserInfoBaseDto() { } + } +} diff --git a/Apimanager_backend/Dtos/UserInfoDto.cs b/Apimanager_backend/Dtos/UserInfoDto.cs new file mode 100644 index 0000000..afe3cca --- /dev/null +++ b/Apimanager_backend/Dtos/UserInfoDto.cs @@ -0,0 +1,15 @@ +using Apimanager_backend.Models; + +namespace Apimanager_backend.Dtos +{ + public class UserInfoDto + { + public int Id { get; set; } + public string UserName { get; set; } + public string Email { get; set; } + public List Roles { get; set; } + public bool IsBan { get; set; } + public decimal Balance { get; set; } + public DateTime Created { get; set; } + } +} diff --git a/Apimanager_backend/Dtos/UserLoginDto.cs b/Apimanager_backend/Dtos/UserLoginDto.cs new file mode 100644 index 0000000..676efce --- /dev/null +++ b/Apimanager_backend/Dtos/UserLoginDto.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Dtos +{ + public class UserLoginDto + { + [MaxLength(20,ErrorMessage = "The maximum username is 20 characters")] + public string UserName { get; set; } + [MaxLength(50,ErrorMessage = "The maximum password is 50 characters")] + [Required(ErrorMessage = "password is required")] + public string Password { get; set; } + } +} diff --git a/Apimanager_backend/Exceptions/BaseException.cs b/Apimanager_backend/Exceptions/BaseException.cs new file mode 100644 index 0000000..a75a609 --- /dev/null +++ b/Apimanager_backend/Exceptions/BaseException.cs @@ -0,0 +1,13 @@ +namespace Apimanager_backend.Exceptions +{ + public class BaseException:Exception + { + public int code; + public string message; + public BaseException(int code, string message):base(message) + { + this.code = code; + this.message = message; + } + } +} diff --git a/Apimanager_backend/Filters/ExceptionFilter/generalExceptionFilter.cs b/Apimanager_backend/Filters/ExceptionFilter/generalExceptionFilter.cs new file mode 100644 index 0000000..67d376f --- /dev/null +++ b/Apimanager_backend/Filters/ExceptionFilter/generalExceptionFilter.cs @@ -0,0 +1,30 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Tools; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Apimanager_backend.Filters.ExceptionFilter +{ + public class generalExceptionFilter : IExceptionFilter + { + public void OnException(ExceptionContext context) + { + if(context.Exception is BaseException) + { + //构造通用错误返回结果 + BaseException exception = (BaseException)context.Exception; + var res = new ResponseBase( + code:exception.code, + message: exception.message, + data:null + ); + int httpCode = StatusCodeHelper.GetHttpStatusCode(exception.code); + context.Result = new JsonResult(res) { + StatusCode = httpCode + }; + context.ExceptionHandled = true; + } + } + } +} diff --git a/Apimanager_backend/Filters/ModelValidationFilter.cs b/Apimanager_backend/Filters/ModelValidationFilter.cs new file mode 100644 index 0000000..7760d2e --- /dev/null +++ b/Apimanager_backend/Filters/ModelValidationFilter.cs @@ -0,0 +1,34 @@ +using Apimanager_backend.Dtos; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Apimanager_backend.Filters +{ + public class ModelValidationFilter :IActionFilter + { + public void OnActionExecuted(ActionExecutedContext context) { } + + public void OnActionExecuting(ActionExecutingContext context) + { + if (!context.ModelState.IsValid) + { + // 将所有的错误信息收集到列表中 + var errors = context.ModelState.Values + .SelectMany(v => v.Errors) + .Select(e => e.ErrorMessage) + .ToList(); + + // 构造统一的响应格式 + var response = new ResponseBase + { + Code = 1001, // 统一的错误码 + Message = errors[0], + Data = null + }; + + // 设置错误响应和 HTTP 状态码 + context.Result = new BadRequestObjectResult(response); + } + } + } +} diff --git a/Apimanager_backend/Migrations/20241024162342_Init-Database.Designer.cs b/Apimanager_backend/Migrations/20241024162342_Init-Database.Designer.cs new file mode 100644 index 0000000..d3bb34c --- /dev/null +++ b/Apimanager_backend/Migrations/20241024162342_Init-Database.Designer.cs @@ -0,0 +1,378 @@ +// +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("20241024162342_Init-Database")] + partial class InitDatabase + { + /// + 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("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.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("Price") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.ToTable("Apipackages"); + }); + + 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("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("Role") + .HasColumnType("int"); + + 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.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.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.Api", b => + { + b.Navigation("ApiCalls"); + }); + + 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("operationLogs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Apimanager_backend/Migrations/20241024162342_Init-Database.cs b/Apimanager_backend/Migrations/20241024162342_Init-Database.cs new file mode 100644 index 0000000..3de7b1b --- /dev/null +++ b/Apimanager_backend/Migrations/20241024162342_Init-Database.cs @@ -0,0 +1,295 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Apimanager_backend.Migrations +{ + /// + public partial class InitDatabase : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Apipackages", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "varchar(20)", maxLength: 20, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CallLimit = table.Column(type: "int", nullable: false), + Price = table.Column(type: "decimal(65,30)", nullable: false), + ExpiryDate = table.Column(type: "datetime(6)", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Apipackages", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Username = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + PassHash = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Role = table.Column(type: "int", nullable: false), + IsBan = table.Column(type: "tinyint(1)", nullable: false), + IsDelete = table.Column(type: "tinyint(1)", nullable: false), + Balance = table.Column(type: "decimal(65,30)", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Apis", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Endpoint = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Method = table.Column(type: "int", nullable: false), + PackageId = table.Column(type: "int", nullable: true), + IsThirdParty = table.Column(type: "tinyint(1)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false), + IsDelete = table.Column(type: "tinyint(1)", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Apis", x => x.Id); + table.ForeignKey( + name: "FK_Apis_Apipackages_PackageId", + column: x => x.PackageId, + principalTable: "Apipackages", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "OperationLogs", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + Operation = table.Column(type: "varchar(20)", maxLength: 20, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TargetType = table.Column(type: "varchar(50)", maxLength: 50, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TargetId = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IpAddress = table.Column(type: "varchar(45)", maxLength: 45, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UserAgent = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Description = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_OperationLogs", x => x.Id); + table.ForeignKey( + name: "FK_OperationLogs_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + OrderNumber = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ThirdPartyOrderId = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Amount = table.Column(type: "decimal(65,30)", nullable: false), + OrderType = table.Column(type: "int", nullable: false), + Status = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false), + Description = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + table.ForeignKey( + name: "FK_Orders_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "UserPackages", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + PackageId = table.Column(type: "int", nullable: false), + RemainingCalls = table.Column(type: "int", nullable: false), + PurchasedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserPackages", x => x.Id); + table.ForeignKey( + name: "FK_UserPackages_Apipackages_PackageId", + column: x => x.PackageId, + principalTable: "Apipackages", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_UserPackages_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "CallLogs", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + ApiId = table.Column(type: "int", nullable: false), + CallTime = table.Column(type: "datetime(6)", nullable: false), + CallResult = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CallLogs", x => x.Id); + table.ForeignKey( + name: "FK_CallLogs_Apis_ApiId", + column: x => x.ApiId, + principalTable: "Apis", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_CallLogs_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Apis_PackageId", + table: "Apis", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_CallLogs_ApiId", + table: "CallLogs", + column: "ApiId"); + + migrationBuilder.CreateIndex( + name: "IX_CallLogs_UserId", + table: "CallLogs", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_OperationLogs_UserId", + table: "OperationLogs", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Orders_OrderNumber", + table: "Orders", + column: "OrderNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Orders_ThirdPartyOrderId", + table: "Orders", + column: "ThirdPartyOrderId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Orders_UserId", + table: "Orders", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_UserPackages_PackageId", + table: "UserPackages", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_UserPackages_UserId", + table: "UserPackages", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Users_Email", + table: "Users", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Users_Username", + table: "Users", + column: "Username", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CallLogs"); + + migrationBuilder.DropTable( + name: "OperationLogs"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "UserPackages"); + + migrationBuilder.DropTable( + name: "Apis"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Apipackages"); + } + } +} diff --git a/Apimanager_backend/Migrations/20241030160723_add-userrole-table.Designer.cs b/Apimanager_backend/Migrations/20241030160723_add-userrole-table.Designer.cs new file mode 100644 index 0000000..1fb1294 --- /dev/null +++ b/Apimanager_backend/Migrations/20241030160723_add-userrole-table.Designer.cs @@ -0,0 +1,408 @@ +// +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("20241030160723_add-userrole-table")] + partial class adduserroletable + { + /// + 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("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.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("Price") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.ToTable("Apipackages"); + }); + + 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("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.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"); + }); + + 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/20241030160723_add-userrole-table.cs b/Apimanager_backend/Migrations/20241030160723_add-userrole-table.cs new file mode 100644 index 0000000..f57483e --- /dev/null +++ b/Apimanager_backend/Migrations/20241030160723_add-userrole-table.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Apimanager_backend.Migrations +{ + /// + public partial class adduserroletable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Role", + table: "Users"); + + migrationBuilder.CreateTable( + name: "UserRoles", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + Role = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_UserRoles", x => x.Id); + table.ForeignKey( + name: "FK_UserRoles_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_UserRoles_UserId", + table: "UserRoles", + column: "UserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserRoles"); + + migrationBuilder.AddColumn( + name: "Role", + table: "Users", + type: "int", + nullable: false, + defaultValue: 0); + } + } +} diff --git a/Apimanager_backend/Migrations/20241103110714_update-logtable.Designer.cs b/Apimanager_backend/Migrations/20241103110714_update-logtable.Designer.cs new file mode 100644 index 0000000..f6857aa --- /dev/null +++ b/Apimanager_backend/Migrations/20241103110714_update-logtable.Designer.cs @@ -0,0 +1,442 @@ +// +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("20241103110714_update-logtable")] + partial class updatelogtable + { + /// + 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("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.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("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("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.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"); + }); + + 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/20241103110714_update-logtable.cs b/Apimanager_backend/Migrations/20241103110714_update-logtable.cs new file mode 100644 index 0000000..81ee381 --- /dev/null +++ b/Apimanager_backend/Migrations/20241103110714_update-logtable.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Apimanager_backend.Migrations +{ + /// + public partial class updatelogtable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} 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/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 new file mode 100644 index 0000000..4cce6c9 --- /dev/null +++ b/Apimanager_backend/Migrations/ApiContextModelSnapshot.cs @@ -0,0 +1,569 @@ +// +using System; +using Apimanager_backend.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Apimanager_backend.Migrations +{ + [DbContext(typeof(ApiContext))] + partial class ApiContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(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/Models/Api.cs b/Apimanager_backend/Models/Api.cs new file mode 100644 index 0000000..be2752a --- /dev/null +++ b/Apimanager_backend/Models/Api.cs @@ -0,0 +1,64 @@ +using Apimanager_backend.Data; +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Models +{ + public class Api + { + /// + /// 主键,自增 + /// + public int Id { get; set; } + + /// + /// API名称 + /// + [MaxLength(200)] + [Required] + public string Name { get; set; } // varchar(20) + /// + /// 描述 + /// + public string Description { get; set; } = string.Empty; + + /// + /// API地址 + /// + [Required] + public string Endpoint { get; set; } // varchar(255) + + /// + /// 调用方法 + /// + public ApiMethod Method { get; set; } // enum('GET','POST','PUT', 'DELETE') + + /// + /// 套餐Id,默认为空(免费无限制) + /// + public int? PackageId { get; set; } // int? 使其可为null + + /// + /// 是否第三方API + /// + public bool IsThirdParty { get; set; } // boolean + + /// + /// 是否启用 + /// + public bool IsActive { get; set; } // boolean + /// + /// 是否删除 + /// + public bool IsDelete { get; set; } // boolean + + /// + /// 创建时间,默认当前时间 + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp + + //导航属性 + public Apipackage? Package { get; set; } + public ICollection ApiCalls { get; set; } + public ICollection ApiRequestExamples { get; set; } + } +} diff --git a/Apimanager_backend/Models/ApiCallLog.cs b/Apimanager_backend/Models/ApiCallLog.cs new file mode 100644 index 0000000..234b42d --- /dev/null +++ b/Apimanager_backend/Models/ApiCallLog.cs @@ -0,0 +1,35 @@ +namespace Apimanager_backend.Models +{ + public class ApiCallLog + { + /// + /// 主键,自增 + /// + public int Id { get; set; } + + /// + /// 外键,用户ID + /// + public int UserId { get; set; } + + /// + /// 外键,API ID + /// + public int ApiId { get; set; } + + /// + /// 调用时间,默认当前时间 + /// + public DateTime CallTime { get; set; } = DateTime.UtcNow; // Timestamp + + /// + /// 调用结果状态码 + /// + public int CallResult { get; set; } // 调用结果状态码 + + + //导航属性 + public User? User { get; set; } + public Api? Api { get; set; } + } +} diff --git a/Apimanager_backend/Models/ApiMethod.cs b/Apimanager_backend/Models/ApiMethod.cs new file mode 100644 index 0000000..c709c94 --- /dev/null +++ b/Apimanager_backend/Models/ApiMethod.cs @@ -0,0 +1,10 @@ +namespace Apimanager_backend.Models +{ + public enum ApiMethod + { + 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 new file mode 100644 index 0000000..2bac923 --- /dev/null +++ b/Apimanager_backend/Models/Apipackage.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Models +{ + public class Apipackage + { + /// + /// 主键,自增 + /// + [Key] + public int Id { get; set; } + + /// + /// 套餐名称 + /// + [MaxLength(20)] + public string Name { get; set; } // varchar(20) + + /// + /// 最大调用次数 + /// + public int CallLimit { get; set; } // int + + /// + /// 价格 + /// + public decimal Price { get; set; } // decimal(10,2) + + + /// + /// 每分钟调用次数限制 + /// + public int OneMinuteLimit { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp + /// + /// 是否删除 + /// + public bool IsDeleted { get; set; } = false; + + //导航属性 + public ICollection Apis { get; set; } + public ICollection Packages { get; set; } + } +} diff --git a/Apimanager_backend/Models/Log.cs b/Apimanager_backend/Models/Log.cs new file mode 100644 index 0000000..ed15d8d --- /dev/null +++ b/Apimanager_backend/Models/Log.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Apimanager_backend.Models +{ + [Table("Logs")] + public class Log + { + public int Id { get; set; } + public DateTime Timestamp { get; set; } + public string Message { get; set; } + public string Exception { get; set; } + public string MessageTemplate { get; set; } + public string Properties { get; set; } + public string LogLevel { get; set; } + + } +} diff --git a/Apimanager_backend/Models/OperationLog.cs b/Apimanager_backend/Models/OperationLog.cs new file mode 100644 index 0000000..1cebed2 --- /dev/null +++ b/Apimanager_backend/Models/OperationLog.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Antiforgery; +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Models +{ + public class OperationLog + { + /// + /// 主键,自增 + /// + public int Id { get; set; } + + /// + /// 操作人ID,操作者的用户ID(通常是管理员) + /// + public int UserId { get; set; } + + /// + /// 操作类型,描述操作的具体内容 + /// + [Required] + [MaxLength(20)] + public string Operation { get; set; } // varchar(20) + + /// + /// 目标类型,操作的对象类型 + /// + [Required] + [MaxLength(50)] + public string TargetType { get; set; } // varchar(50) + + /// + /// 目标对象ID,操作对象的具体ID + /// + public int TargetId { get; set; } + + /// + /// 操作时间,操作发生时间 + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp + + /// + /// 操作来源IP + /// + [MaxLength(45)] + public string IpAddress { get; set; } // varchar(45) + + /// + /// 操作设备信息 + /// + public string UserAgent { get; set; } // varchar(255) + + /// + /// 操作描述,可选的详细说明,解释操作原因等 + /// + public string Description { get; set; } // varchar(255) + + //导航属性 + public User? User { get; set; } + } +} diff --git a/Apimanager_backend/Models/Order.cs b/Apimanager_backend/Models/Order.cs new file mode 100644 index 0000000..804b846 --- /dev/null +++ b/Apimanager_backend/Models/Order.cs @@ -0,0 +1,62 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Models +{ + public class Order + { + /// + /// 主键,自增 + /// + public int Id { get; set; } + + /// + /// 外键,用户ID + /// + public int UserId { get; set; } + + /// + /// 订单号,唯一 + /// + [Required] + public string OrderNumber { get; set; } // varchar(50) + + /// + /// 第三方系统订单编号 + /// + public string? ThirdPartyOrderId { get; set; } // varchar(100) + + /// + /// 订单金额 + /// + public decimal Amount { get; set; } // decimal(10, 2) + + /// + /// 订单类型 + /// + public OrderType OrderType { get; set; } // enum('Recharge','Purchase','Refund') + + /// + /// 订单状态 + /// + public OrderStatus Status { get; set; } // enum('Pending','Completed','Cancelled','Failed') + + /// + /// 创建时间,订单创建时间 + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // timestamp + + /// + /// 更新时间,订单状态更新时间 + /// + public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; // timestamp + + /// + /// 订单描述,可选的详细信息 + /// + public string? Description { get; set; } // varchar(255) + + //导航属性 + public User? User { get; set; } + } +} diff --git a/Apimanager_backend/Models/OrderStatus.cs b/Apimanager_backend/Models/OrderStatus.cs new file mode 100644 index 0000000..9477196 --- /dev/null +++ b/Apimanager_backend/Models/OrderStatus.cs @@ -0,0 +1,10 @@ +namespace Apimanager_backend.Models +{ + public enum OrderStatus + { + Pending, + Completed, + Cancelled, + Failed + } +} diff --git a/Apimanager_backend/Models/OrderType.cs b/Apimanager_backend/Models/OrderType.cs new file mode 100644 index 0000000..d3f8f22 --- /dev/null +++ b/Apimanager_backend/Models/OrderType.cs @@ -0,0 +1,9 @@ +namespace Apimanager_backend.Models +{ + public enum OrderType + { + Recharge, + Purchase, + Refund + } +} 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 new file mode 100644 index 0000000..5e71322 --- /dev/null +++ b/Apimanager_backend/Models/User.cs @@ -0,0 +1,73 @@ +using System.ComponentModel.DataAnnotations; + +namespace Apimanager_backend.Models +{ + public class User + { + /// + /// 用户ID,主键,自增 + /// + public int Id { get; set; } + + /// + /// 用户名,唯一 + /// + [Required] + public string Username { get; set; } // varchar(20) + + /// + /// 邮箱,唯一 + /// + public string Email { get; set; } // varchar(20) + + /// + /// 密码哈希 + /// + [Required] + [MaxLength(255)] + public string PassHash { get; set; } // varchar(255) + + /// + /// 用户角色 + /// + //public UserRole Role { get; set; } // Enum('Admin','User') + + /// + /// 是否禁用 + /// + public bool IsBan { get; set; } = false; // boolean + /// + /// 是否删除 + /// + public bool IsDelete { get; set; } = false; // boolean + + /// + /// 余额 + /// + public decimal Balance { get; set; } = 0; // Decimal(10) + /// + /// api调用凭证 + /// + public string? ApiKey { get; set; } = null; + + /// + /// 创建时间,默认当前时间 + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // Timestamp + + //导航属性 + public ICollection Packages { get; set; } + public ICollection operationLogs { get; set; } + 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/Models/UserPackage.cs b/Apimanager_backend/Models/UserPackage.cs new file mode 100644 index 0000000..f94b7f5 --- /dev/null +++ b/Apimanager_backend/Models/UserPackage.cs @@ -0,0 +1,36 @@ +namespace Apimanager_backend.Models +{ + public class UserPackage + {/// + /// 主键,自增 + /// + public int Id { get; set; } + + /// + /// 外键,用户ID + /// + public int UserId { get; set; } + + /// + /// 外键,套餐ID + /// + public int PackageId { get; set; } + + /// + /// 剩余调用次数 + /// + public int RemainingCalls { get; set; } + /// + /// 套餐过期时间,可用于控制套餐是否过期 + /// + public DateTime ExpiryDate { get; set; } // timestamp + /// + /// 购买时间 + /// + public DateTime PurchasedAt { get; set; } = DateTime.UtcNow; // timestamp + + //导航属性 + public User? User { get; set; } + public Apipackage? Package { get; set; } + } +} diff --git a/Apimanager_backend/Models/UserRole.cs b/Apimanager_backend/Models/UserRole.cs new file mode 100644 index 0000000..1df0b7a --- /dev/null +++ b/Apimanager_backend/Models/UserRole.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Apimanager_backend.Models +{ + public class UserRole + { + public int Id { get; set; } + public int UserId { get; set; } + public string Role { get; set; } + + //导航属性 + [JsonIgnore] + public User User { get; set; } + } +} diff --git a/Apimanager_backend/Program.cs b/Apimanager_backend/Program.cs new file mode 100644 index 0000000..e0c8391 --- /dev/null +++ b/Apimanager_backend/Program.cs @@ -0,0 +1,71 @@ +using Apimanager_backend.Config; +using Apimanager_backend.Data; +using Apimanager_backend.Filters; +using Apimanager_backend.Filters.ExceptionFilter; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Serilog; +using Serilog.Sinks.MariaDB.Extensions; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +IConfiguration configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .Build(); +string? redStr = configuration["Redis:ConnectionString"]; +string? constr = configuration.GetConnectionString("DefaultConnection"); +//־ +/* +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .WriteTo.MariaDB( + connectionString: constr, + tableName: "Logs", + autoCreateTable:true + ).CreateLogger(); +builder.Host.UseSerilog(); +*/ +builder.Services.AddDbContext(option => +option.UseMySql(constr, MySqlServerVersion.AutoDetect(constr)) +); +builder.Services.AddAllService(configuration); +builder.Services.AddControllers(options => +{ + //ģ֤ + options.Filters.Add(); + //Exception + options.Filters.Add(); +}).ConfigureApiBehaviorOptions(option => +{ + option.SuppressModelStateInvalidFilter = true; +}) +.AddJsonOptions(options => +{ + options.JsonSerializerOptions.MaxDepth = 64; +}); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseCors("AllowAll"); +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/Apimanager_backend/Properties/launchSettings.json b/Apimanager_backend/Properties/launchSettings.json new file mode 100644 index 0000000..dd75711 --- /dev/null +++ b/Apimanager_backend/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:1132", + "sslPort": 44318 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5292", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7031;http://localhost:5292", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Apimanager_backend/Services/AdminService.cs b/Apimanager_backend/Services/AdminService.cs new file mode 100644 index 0000000..03b76bd --- /dev/null +++ b/Apimanager_backend/Services/AdminService.cs @@ -0,0 +1,118 @@ +using Apimanager_backend.Data; +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Models; +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel; + +namespace Apimanager_backend.Services +{ + public class AdminService : IAdminService + { + private readonly ApiContext context; + private readonly IMapper mapper; + private readonly ILogger logger; + public AdminService(ApiContext context, IMapper mapper, ILogger logger) + { + this.context = context; + this.mapper = mapper; + this.logger = logger; + } + #region 禁用用户 + public async Task BanUserAsync(int userId) + { + var user = await context.Users.FirstOrDefaultAsync(x => x.Id == userId); + if (user == null) + { + throw new BaseException(2004,"用户不存在"); + } + user.IsBan = true; + context.Users.Update(user); + await context.SaveChangesAsync(); + } + #endregion + #region 新建用户 + public async Task CreateUserAsync(CreateUserDto dto) + { + //添加用户 + var user = mapper.Map(dto); + context.Users.Add(user); + await context.SaveChangesAsync(); + //添加默认角色 + UserRole userRole = new UserRole + { + UserId = user.Id, + Role = "User" + }; + + context.UserRoles.Add(userRole); + await context.SaveChangesAsync(); + return mapper.Map(user); + } + #endregion + #region 删除用户 + public async Task DeleteUserAsync(int userId) + { + var user = await context.Users.FirstOrDefaultAsync(x => x.Id == userId); + if (user == null) + { + throw new BaseException(2004, "用户不存在"); + } + user.IsDelete = true; + context.Users.Update(user); + await context.SaveChangesAsync(); + } + #endregion + #region 获取用户列表 + public async Task> GetUsersAsync(int page, int pageSize, bool desc) + { + var query = context.Users.Include(x => x.Roles).Where(x => true) + .OrderBy(x => x.Id); + //倒序 + if (desc) + { + query = query.OrderByDescending(x => x.Id); + } + //分页 + var users = await query.Skip((page - 1) * pageSize) + .Take(pageSize).ToListAsync(); + return mapper.Map>(users); + } + #endregion + #region 禁用用户 + public async Task UnbanUserAsync(int userId) + { + var user = await context.Users.FirstOrDefaultAsync(x => x.Id == userId); + if (user == null) + { + throw new BaseException(2004, "用户不存在"); + } + user.IsBan = false; + context.Users.Update(user); + await context.SaveChangesAsync(); + } + #endregion + #region 更新用户信息 + public async Task UpdateUserAsync(int userId,AdminUpdateUserDto dto) + { + var user = await context.Users.FirstOrDefaultAsync(x => x.Id == userId); + if(user == null) + { + throw new BaseException(2004,"用户不存在"); + } + 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/AuthService.cs b/Apimanager_backend/Services/AuthService.cs new file mode 100644 index 0000000..d4993e2 --- /dev/null +++ b/Apimanager_backend/Services/AuthService.cs @@ -0,0 +1,109 @@ +using Apimanager_backend.Data; +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Models; +using Apimanager_backend.Tools; +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using StackExchange.Redis; + +namespace Apimanager_backend.Services +{ + public class AuthService:IAuthService + { + private readonly ApiContext apiContext; + private readonly ILogger logger; + private readonly IConnectionMultiplexer redis; + private readonly IEmailService emailService; + private readonly IMapper mapper; + private readonly int DbIndex = 1; + public AuthService(ApiContext apiContext, IMapper automapper,ILogger logger,IConnectionMultiplexer redis,IEmailService emailService) + { + this.apiContext = apiContext; + this.mapper = automapper; + this.logger = logger; + this.redis = redis; + this.emailService = emailService; + } + #region 用户登录 + public async Task LoginAsync(string username, string password) + { + //查找用户 + User? user = await apiContext.Users.Include(x => x.Roles).SingleOrDefaultAsync(x => + x.Username == username && x.PassHash == password + ); + + //用户不存在或密码错误都为登录失败 + if (user == null) + { + throw new BaseException(2001, "Invalid username or password"); + } + + //用户被禁用 + if (user.IsBan) + { + throw new BaseException(2002, "User account is disabled"); + } + + return mapper.Map(user); + } + #endregion + #region 用户注册 + public async Task RegisterAsync(RegisterRequestDto dto) + { + var db = redis.GetDatabase(DbIndex); + //获取邮箱对应验证码 + var code = await db.StringGetAsync(dto.Email); + if(!code.HasValue || code.ToString() != dto.VerificationCode) + { + throw new BaseException(5005,"验证码错误"); + } + User user = new User + { + Username = dto.Username, + PassHash = dto.Password, + Email = dto.Email, + IsBan = false, + IsDelete = false, + Balance = 0, + }; + try + { + //添加新用户 + await apiContext.Users.AddAsync(user); + await apiContext.SaveChangesAsync(); + UserRole userRole = new UserRole + { + UserId = user.Id, + Role = "User" + }; + await apiContext.UserRoles.AddAsync(userRole); + await apiContext.SaveChangesAsync(); + return mapper.Map(user); + }catch(Exception e) + { + throw new BaseException(1005,e.Message); + } + + } + #endregion + #region 发送注册验证码 + public async Task SendRegisterCodeAsync(string email) + { + //生成随机码 + string code = RandomCodeHelper.GetRandomCodeStr(); + string subject = "注册验证码"; + string body = $"您的注册验证码为:{code}
有效期60分钟!"; + //随机码写入redis + var db = redis.GetDatabase(DbIndex); + bool redisSuccess = await db.StringSetAsync(email,code,TimeSpan.FromHours(1)); + if (!redisSuccess) + { + throw new BaseException(1005,"Redis Str Set Error"); + } + //发送邮件 + await emailService.SendEmailAsync(email,subject,body); + } + #endregion + } +} diff --git a/Apimanager_backend/Services/EmailSerivce.cs b/Apimanager_backend/Services/EmailSerivce.cs new file mode 100644 index 0000000..7205208 --- /dev/null +++ b/Apimanager_backend/Services/EmailSerivce.cs @@ -0,0 +1,49 @@ +using Apimanager_backend.Exceptions; +using System.Net; +using System.Net.Mail; + +namespace Apimanager_backend.Services +{ + public class EmailService:IEmailService + { + private readonly IConfiguration _configuration; + public EmailService(IConfiguration configuration) + { + _configuration = configuration; + SmtpHost = _configuration["EmailSettings:Server"]; + Port = int.Parse(_configuration["EmailSettings:Port"]); + Username = _configuration["EmailSettings:Username"]; + Password = _configuration["EmailSettings:Password"]; + EnableSSL = bool.Parse(_configuration["EmailSettings:Ssl"]); + } + private string SmtpHost { get; set; } + private int Port { get; set; } + public bool EnableSSL { get; set; } + private string Username { get; set; } + private string Password { get; set; } + public async Task SendEmailAsync(string toEmail,string subject,string body) + { + try + { + using SmtpClient smtpClient = new SmtpClient(SmtpHost, Port) + { + Credentials = new NetworkCredential(Username, Password), + EnableSsl = EnableSSL, //启用ssl + Timeout = 30000 + }; + using var emailMessage = new MailMessage + { + From = new MailAddress(Username), + Subject = subject, + Body = body, + IsBodyHtml = true + }; + emailMessage.To.Add(toEmail); + await smtpClient.SendMailAsync(emailMessage); + }catch(Exception e) + { + throw new BaseException(5004,e.Message); + } + } + } +} diff --git a/Apimanager_backend/Services/IAdminService.cs b/Apimanager_backend/Services/IAdminService.cs new file mode 100644 index 0000000..a9115b6 --- /dev/null +++ b/Apimanager_backend/Services/IAdminService.cs @@ -0,0 +1,50 @@ +using Apimanager_backend.Dtos; + +namespace Apimanager_backend.Services +{ + public interface IAdminService + { + /// + /// 禁用用户,使其无法登录。 + /// + /// 要禁用的用户ID + /// 异步操作 + Task BanUserAsync(int userId); + /// + /// 取消禁用用户,恢复登录权限。 + /// + /// 要取消禁用的用户ID + /// 异步操作 + Task UnbanUserAsync(int userId); + /// + /// 获取分页的用户列表。 + /// + /// 要获取的页码,从1开始 + /// 每页的用户数量 + /// 是否按降序排序 + /// 包含用户信息的 + Task> GetUsersAsync(int page, int pageSize, bool desc); + /// + /// 创建新用户。 + /// + /// 包含新用户信息的 + /// 创建成功的用户信息 + Task CreateUserAsync(CreateUserDto user); + /// + /// 删除指定的用户。 + /// + /// 用户ID + /// 异步操作 + Task DeleteUserAsync(int userId); + /// + /// 修改用户信息 + /// + /// + 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/IAuthService.cs b/Apimanager_backend/Services/IAuthService.cs new file mode 100644 index 0000000..19d7ab4 --- /dev/null +++ b/Apimanager_backend/Services/IAuthService.cs @@ -0,0 +1,27 @@ +using Apimanager_backend.Dtos; + +namespace Apimanager_backend.Services +{ + public interface IAuthService + { + /// + /// 登录用户,根据用户名和密码进行身份验证。 + /// + /// 用户名 + /// 密码 + /// 包含用户信息的 + Task LoginAsync(string username, string password); + /// + /// 用户注册邮箱验证码 + /// + /// + /// + Task SendRegisterCodeAsync(string email); + /// + /// 用户注册 + /// + /// + /// + Task RegisterAsync(RegisterRequestDto dto); + } +} diff --git a/Apimanager_backend/Services/IEmailService.cs b/Apimanager_backend/Services/IEmailService.cs new file mode 100644 index 0000000..3dac127 --- /dev/null +++ b/Apimanager_backend/Services/IEmailService.cs @@ -0,0 +1,14 @@ +namespace Apimanager_backend.Services +{ + public interface IEmailService + { + /// + /// 发送邮件 + /// + /// 收件人邮箱 + /// 主题 + /// 正文 + /// + public Task SendEmailAsync(string toEmail,string subject,string body); + } +} diff --git a/Apimanager_backend/Services/IPackageService.cs b/Apimanager_backend/Services/IPackageService.cs new file mode 100644 index 0000000..f5beb16 --- /dev/null +++ b/Apimanager_backend/Services/IPackageService.cs @@ -0,0 +1,65 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Models; + +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); + /// + /// 获取用户所有订阅套餐 + /// + /// + /// + 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/IRefreshTokenService.cs b/Apimanager_backend/Services/IRefreshTokenService.cs new file mode 100644 index 0000000..577078e --- /dev/null +++ b/Apimanager_backend/Services/IRefreshTokenService.cs @@ -0,0 +1,30 @@ +namespace Apimanager_backend.Services +{ + public interface IRefreshTokenService + { + /// + /// 创建刷新令牌 + /// + /// 用户id + /// 刷新令牌 + Task CreateRefereshTokenAsync(string userId); + /// + /// 验证刷新令牌 + /// + /// 刷新令牌 + /// 是否验证通过 + Task ValidateRefreshTokenAsync(string userId,string refreshToken); + /// + /// 删除刷新令牌 + /// + /// 刷新令牌 + /// 是否删除成功 + Task DeleterRefreshTokenAsync(string userId); + /// + /// 更新刷新令牌有效期 + /// + /// 用户id + /// 是否成功 + Task UpdateRefreshTokenAsync(string userId); + } +} 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/ITokenService.cs b/Apimanager_backend/Services/ITokenService.cs new file mode 100644 index 0000000..1016997 --- /dev/null +++ b/Apimanager_backend/Services/ITokenService.cs @@ -0,0 +1,16 @@ +using Apimanager_backend.Models; + +namespace Apimanager_backend.Services +{ + public interface ITokenService + { + /// + /// 拥护凭证 + /// + /// 用户ID + /// 用户名 + /// 角色 + /// token + string GenerateAccessToken(string userId, List roles); + } +} diff --git a/Apimanager_backend/Services/IUserService.cs b/Apimanager_backend/Services/IUserService.cs new file mode 100644 index 0000000..7da04dc --- /dev/null +++ b/Apimanager_backend/Services/IUserService.cs @@ -0,0 +1,51 @@ +using Apimanager_backend.Dtos; +using Apimanager_backend.Models; +using System.Runtime.CompilerServices; + +namespace Apimanager_backend.Services +{ + public interface IUserService + { + /// + /// 发送密码重置邮件到指定邮箱。 + /// + /// 用户注册的邮箱地址 + /// 异步操作 + Task SendResetPasswordEmailAsync(string email); + + /// + /// 重置用户密码,验证重置令牌的有效性并更新密码。 + /// + /// 用户邮箱地址 + /// 重置密码的令牌 + /// 新的密码 + /// 异步操作 + Task ResetPasswordAsync(string email, string code, string newPassword); + + /// + /// 获取用户信息。 + /// + /// 用户ID + /// 包含用户信息的 + Task GetUserAsync(int userId); + + /// + /// 更新用户信息。 + /// + /// 包含更新信息的 + /// 更新后的 + Task UpdateUserAsync(int userId,UpdateUserDto user); + /// + /// 检测用户名是否被使用 + /// + /// 用户名 + /// + Task IsUsernameExist(string username); + /// + /// 检测邮箱是否被使用 + /// + /// 邮箱 + /// + Task IsEmailExist(string email); + } +} 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/RefreshTokenService.cs b/Apimanager_backend/Services/RefreshTokenService.cs new file mode 100644 index 0000000..ea58d97 --- /dev/null +++ b/Apimanager_backend/Services/RefreshTokenService.cs @@ -0,0 +1,78 @@ +using Apimanager_backend.Exceptions; +using StackExchange.Redis; + +namespace Apimanager_backend.Services +{ + public class RefreshTokenService : IRefreshTokenService + { + private readonly IConnectionMultiplexer redis; + private readonly IConfiguration configuration; + private readonly int DbIndex = 0; + public RefreshTokenService(IConnectionMultiplexer redis, IConfiguration configuration) + { + this.redis = redis; + this.configuration = configuration; + } + #region 创建刷新令牌 + public async Task CreateRefereshTokenAsync(string userId) + { + var refreshToken = Guid.NewGuid().ToString(); + var expiryDays = Convert.ToDouble(configuration["JwtSettings:RefreshTokenExpiryDays"]); + + // 保存到Redis,设置过期时间 + var db = redis.GetDatabase(DbIndex); + var res = await db.StringSetAsync( userId , refreshToken, TimeSpan.FromDays(expiryDays)); + if (!res) + { + throw new BaseException(1006, "Service unavailable"); + } + return refreshToken; + } + #endregion + #region 删除刷新令牌 + public async Task DeleterRefreshTokenAsync(string userId) + { + var db = redis.GetDatabase(DbIndex); + bool res = await db.KeyDeleteAsync(userId); + if (!res) + { + throw new BaseException(1006, "Service unavailable"); + } + } + #endregion + #region 刷新令牌有效期 + public async Task UpdateRefreshTokenAsync(string userId) + { + var db = redis.GetDatabase(DbIndex); + var expiryDays = Convert.ToDouble(configuration["JwtSettings:RefreshTokenExpiryDays"]); + //获取refresh剩余有效时间 + var time =await db.KeyTimeToLiveAsync(userId); + //判断有效时间是否大于零天小于三天,否则不刷新有效期 + if(time <= TimeSpan.Zero || time >= TimeSpan.FromDays(3)) + { + return; + } + //刷新过期时间 + await db.KeyExpireAsync(userId,TimeSpan.FromDays(expiryDays)); + } + #endregion + #region 验证令牌 + public async Task ValidateRefreshTokenAsync(string userId,string refreshToken) + { + var db = redis.GetDatabase(DbIndex); + var redisValue = await db.StringGetAsync(userId); + //验证refreshToken是否存在 + if (!redisValue.HasValue) + { + return false; + } + string refreshTokenTrue = redisValue.ToString(); + if (!refreshToken.Equals(refreshTokenTrue)) + { + return false; + } + return true; + } + #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/TokenService.cs b/Apimanager_backend/Services/TokenService.cs new file mode 100644 index 0000000..28215a5 --- /dev/null +++ b/Apimanager_backend/Services/TokenService.cs @@ -0,0 +1,50 @@ + +using Apimanager_backend.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; + +namespace Apimanager_backend.Services +{ + public class TokenService:ITokenService + { + public readonly IConfiguration configuration; + public TokenService(IConfiguration configuration) + { + this.configuration = configuration; + } + + public string GenerateAccessToken(string userId,List roles) + { + var jwtSettings = configuration.GetSection("JwtSettings"); + + // 创建Claims列表,包含用户名和角色信息 + var claims = new List + { + new Claim("userId", userId), // 使用userId作为唯一标识 + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }; + //添加用户角色 + foreach(var role in roles) + { + var claim = new Claim(ClaimTypes.Role, role.Role.ToString()); + claims.Add(claim); + } + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Secret"])); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var token = new JwtSecurityToken( + issuer: jwtSettings["Issuer"], + audience: jwtSettings["Audience"], + claims: claims, + expires: DateTime.Now.AddMinutes(Convert.ToDouble(jwtSettings["AccessTokenExpiryMinutes"])), + signingCredentials: creds); + + return new JwtSecurityTokenHandler().WriteToken(token); + } + } +} diff --git a/Apimanager_backend/Services/UserService.cs b/Apimanager_backend/Services/UserService.cs new file mode 100644 index 0000000..95a583f --- /dev/null +++ b/Apimanager_backend/Services/UserService.cs @@ -0,0 +1,105 @@ +using Apimanager_backend.Config; +using Apimanager_backend.Data; +using Apimanager_backend.Dtos; +using Apimanager_backend.Exceptions; +using Apimanager_backend.Models; +using Apimanager_backend.Tools; +using AutoMapper; +using Microsoft.AspNetCore.Connections.Features; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using StackExchange.Redis; +using System.ComponentModel; + +namespace Apimanager_backend.Services +{ + public class UserService : IUserService + { + private readonly ApiContext apiContext; + private readonly IMapper mapper; + private readonly ILogger logger; + private readonly IConnectionMultiplexer redis; + private readonly IEmailService emailService; + private readonly int DbSet = 2; + public UserService(ApiContext apiContext,IMapper automapper,ILogger logger,IConnectionMultiplexer redis,IEmailService emailService) + { + this.apiContext = apiContext; + this.mapper = automapper; + this.logger = logger; + this.redis = redis; + this.emailService = emailService; + } + public async Task GetUserAsync(int userId) + { + User? user = await apiContext.Users.Include(x => x.Roles).SingleOrDefaultAsync(x => x.Id == userId); + //未找到用户 + if (user == null) + { + throw new BaseException(2004, "User not found"); + } + return mapper.Map(user); + } + + public async Task IsEmailExist(string email) + { + return await apiContext.Users.AnyAsync(x => x.Email == email); + } + + public async Task IsUsernameExist(string username) + { + return await apiContext.Users.AnyAsync(x => x.Username == username); + } + + public async Task ResetPasswordAsync(string email, string code, string newPassword) + { + //校验验证码 + var db = redis.GetDatabase(DbSet); + var value = await db.StringGetAsync(email); + if (!value.HasValue || value.ToString() != code) + { + throw new BaseException(5005, "验证码错误"); + } + //验证成功,开始重置流程 + var user = await apiContext.Users.FirstOrDefaultAsync(x => x.Email == email); + if(user == null) + { + throw new BaseException(2004, "用户不存在"); + } + //修改密码 + user.PassHash = newPassword; + apiContext.Users.Update(user); + await apiContext.SaveChangesAsync(); + } + #region 发送密码重置邮件 + public async Task SendResetPasswordEmailAsync(string email) + { + var randomCode = RandomCodeHelper.GetRandomCodeStr(); + //记录到redis + var db = redis.GetDatabase(DbSet); + bool redisSuccess = await db.StringSetAsync(email,randomCode,TimeSpan.FromHours(1)); + if (!redisSuccess) + { + throw new BaseException(1005, "Redis Str Set Error"); + } + string subject = "重置验证码"; + string body = $"您的重置验证码为:{randomCode}
有效期60分钟!"; + //发送邮件 + await emailService.SendEmailAsync(email,subject,body); + } + #endregion + + public async Task UpdateUserAsync(int userId,UpdateUserDto dto) + { + var user = await apiContext.Users.FirstOrDefaultAsync(x => x.Id == userId); + if (user == null) + { + throw new BaseException(2004, "用户不存在"); + } + user.PassHash = dto.password == null ? user.PassHash : dto.password; + + apiContext.Users.Update(user); + await apiContext.SaveChangesAsync(); + return mapper.Map(user); + } + } +} diff --git a/Apimanager_backend/Tools/HashHelper.cs b/Apimanager_backend/Tools/HashHelper.cs new file mode 100644 index 0000000..4898bae --- /dev/null +++ b/Apimanager_backend/Tools/HashHelper.cs @@ -0,0 +1,6 @@ +namespace Apimanager_backend.Tools +{ + public class HashHelper + { + } +} diff --git a/Apimanager_backend/Tools/RandomCodeHelper.cs b/Apimanager_backend/Tools/RandomCodeHelper.cs new file mode 100644 index 0000000..3392290 --- /dev/null +++ b/Apimanager_backend/Tools/RandomCodeHelper.cs @@ -0,0 +1,15 @@ +namespace Apimanager_backend.Tools +{ + public static class RandomCodeHelper + { + /// + /// 生成随机数字符串 + /// + /// + public static string GetRandomCodeStr() + { + Random random = new Random(); + return random.Next(10000, 99999).ToString(); + } + } +} diff --git a/Apimanager_backend/Tools/StatusCodeHelper.cs b/Apimanager_backend/Tools/StatusCodeHelper.cs new file mode 100644 index 0000000..e0728a6 --- /dev/null +++ b/Apimanager_backend/Tools/StatusCodeHelper.cs @@ -0,0 +1,133 @@ +namespace Apimanager_backend.Tools +{ + public static class StatusCodeHelper + { + public static int GetHttpStatusCode(int customErrorCode) + { + int httpStatusCode; + + switch (customErrorCode) + { + // 通用错误码 + case 1000: // 成功 + httpStatusCode = 200; + break; + case 1001: // 参数错误 + httpStatusCode = 400; + break; + case 1002: // 用户未登录或认证失败 + httpStatusCode = 401; + break; + case 1003: // 无权限访问 + httpStatusCode = 403; + break; + case 1004: // 资源未找到 + httpStatusCode = 404; + break; + case 1005: // 服务器内部错误 + httpStatusCode = 500; + break; + case 1006: // 服务暂时不可用 + httpStatusCode = 503; + break; + + // 用户模块错误码 + case 2000: // 登录成功 + httpStatusCode = 200; + break; + case 2001: // 用户名或密码错误 + httpStatusCode = 401; + break; + case 2002: // 用户账户被禁用 + httpStatusCode = 401; + break; + case 2003: // 用户名已存在 + httpStatusCode = 409; + break; + case 2004: // 用户不存在 + httpStatusCode = 404; + break; + case 2005: // 邮箱已存在 + httpStatusCode = 409; + break; + case 2006: // 用户无权限进行该操作 + httpStatusCode = 403; + break; + case 2007: // 密码重置失败 + httpStatusCode = 400; + break; + case 2008: // 凭证到期或无效 + httpStatusCode = 403; + break; + case 2009: // 刷新令牌到期或无效 + httpStatusCode = 403; + break; + + // API模块错误码 + case 3000: // API调用成功 + httpStatusCode = 200; + break; + case 3001: // API访问受限 + httpStatusCode = 403; + break; + case 3002: // API不存在 + httpStatusCode = 404; + break; + case 3003: // API调用次数超限 + httpStatusCode = 429; + break; + case 3004: // 未购买该API套餐或权限不足 + httpStatusCode = 403; + break; + case 3005: // API调用失败,服务器错误 + httpStatusCode = 500; + break; + + // 套餐与支付模块错误码 + case 4000: // 支付成功 + httpStatusCode = 200; + break; + case 4001: // 支付请求无效 + httpStatusCode = 400; + break; + case 4002: // 支付失败,余额不足 + httpStatusCode = 402; + break; + case 4003: // 订单未找到 + httpStatusCode = 404; + break; + case 4004: // 重复支付或订单冲突 + httpStatusCode = 409; + break; + case 4005: // 支付系统错误 + httpStatusCode = 500; + break; + case 4006: // 退款成功 + httpStatusCode = 200; + break; + + // 日志与系统模块错误码 + case 5000: // 日志查询成功 + httpStatusCode = 200; + break; + case 5001: // 日志记录未找到 + httpStatusCode = 404; + break; + case 5002: // 日志服务异常 + httpStatusCode = 500; + break; + case 5003: // 无权限查看操作日志 + httpStatusCode = 403; + break; + + default: + // 未知错误码,返回 500 + httpStatusCode = 500; + break; + } + + return httpStatusCode; + } + + } +} diff --git a/Apimanager_backend/appsettings.Development.json b/Apimanager_backend/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Apimanager_backend/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Apimanager_backend/appsettings.json b/Apimanager_backend/appsettings.json new file mode 100644 index 0000000..eb6d675 --- /dev/null +++ b/Apimanager_backend/appsettings.json @@ -0,0 +1,29 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "DefaultConnection": "server=192.168.5.200;username=root;password=768788Dyw@;port=3306;database=api_billing_system;SslMode=Preferred;" + }, + "JwtSettings": { + "Secret": "deXtdXode6hv0SI1o6xRw1ALkn0vYsWn", + "Issuer": "qinglan", + "Audience": "qinglan", + "AccessTokenExpiryMinutes": 60, + "RefreshTokenExpiryDays": 7 + }, + "redis": { + "ConnectionString": "192.168.5.200:6379" + }, + "EmailSettings": { + "Server": "smtp.exmail.qq.com", + "Port": "587", + "Ssl": true, + "Username": "nanxun@nxsir.cn", + "Password": "2919054393Dyw" + } +} diff --git a/DatabaseDefine.md b/DatabaseDefine.md new file mode 100644 index 0000000..3b0ad48 --- /dev/null +++ b/DatabaseDefine.md @@ -0,0 +1,93 @@ +# 数据库定义 + +### 用户表(Users) + +| 字段名 | 类型 | 描述 | 备注 | +| --------- | -------------------- | -------- | ------------ | +| Id | int | 用户ID | 主键自增 | +| Username | varchar(20) | 用户名 | 唯一 | +| Email | varchar(20) | 邮箱 | 唯一 | +| PassHash | varchar(255) | 密码哈希 | | +| Role | Enum('Admin','User') | 用户角色 | | +| IsBan | boolean | 是否禁用 | | +| IsDelete | boolean | 是否删除 | | +| Balance | Decimal(10) | 余额 | | +| CreatedAt | Timestamp | 创建时间 | 默认当前时间 | + +### API表(Apis) + +| 字段名 | 数据类型 | 描述 | 备注 | +| ------------ | ------------------------------- | ------------- | ---------------------- | +| Id | int | 主键,自增 | | +| Name | varchar(20) | api名称 | | +| Endpoint | varchar(255) | api地址 | | +| Method | enum('GET','POST','PUT',DELETE) | 调用方法 | | +| PackageId | int | 套餐Id | 默认为空(免费无限制) | +| IsThirdParty | boolean | 是否第三方API | | +| IsActive | boolean | 是否启用 | | +| isDelete | boolean | 是否删除 | | +| CreatedAt | timestamp | 创建时间 | 默认当前时间 | + +### API调用表(ApiCallLogs) + +| 字段名 | 数据类型 | 描述 | 备注 | +| ---------- | --------- | -------------- | ------------ | +| Id | int | 主键,自增 | | +| UserId | int | 外键 | 用户ID | +| ApiId | int | 外键 | APIID | +| CallTime | timestamp | 调用时间 | 默认当前时间 | +| CallResult | int | 调用结果状态码 | | + +### 订单表(Orders) + +| 字段名 | 数据类型 | 描述 | 备注 | +| ----------------- | ------------------------------------------------ | ------------------ | -------------------------------- | +| Id | int | 主键,自增 | | +| UserId | int | 外键 | 用户ID | +| OrderNumber | varchar(50) | 订单号 | 唯一订单编号,适合与外部系统对接 | +| ThirdPartyOrderId | varchar(100) | 第三方系统订单编号 | 可用于追踪第三方支付 | +| Amount | decimal(10,2) | 订单金额 | 正数充值或消费,负数表示退款 | +| OrderType | enum('Recharge','Purchase', 'Refund') | 订单类型 | 充值、购买、退款等 | +| Status | enum('Pending','Completed','Cancelled','Failed') | 订单状态 | 订单状态:进行中、完成、取消等 | +| CreatedAt | timestamp | 创建时间 | 订单创建时间 | +| UpdatedAt | timestamp | 更新时间 | 订单状态更新时间 | +| Description | varchar(255) | 订单描述 | 可选的详细信息,解释订单用途 | + +### 操作日志表(OperationLogs) + +| 字段名 | 数据类型 | 描述 | 备注 | +| ----------- | ------------ | ------------ | ---------------------------------- | +| Id | int | 主键,自增 | | +| UserId | int | 操作人ID | 操作者的用户ID(通常是管理员) | +| Operation | varchar(20) | 操作类型 | 描述操作的具体内容,例如“禁用用户” | +| TargetType | varchar(50) | 目标类型 | 操作的对象类型,例如“用户” | +| TargetId | int | 目标对象ID | 操作对象的具体ID | +| CreatedAt | timestamp | 操作时间 | 操作发生时间 | +| IpAddress | varchar(45) | 操作来源IP | | +| UserAgent | varchar(255) | 操作设备信息 | | +| Description | varchar(255) | 操作描述 | 可选的详细说明,解释操作原因等 | + +### 套餐表(ApiPackages) + +| 字段名 | 数据类型 | 描述 | 备注 | +| ---------- | ------------- | ------------ | ---------------------- | +| Id | int | 主键,自增 | | +| Name | varchar(20) | 套餐名称 | | +| CallLimit | int | 最大调用次数 | | +| Price | decimal(10,2) | 价格 | | +| ExpiryDate | timestamp | 套餐过期时间 | 可用于控制套餐是否过期 | +| CreatedAt | timestamp | 创建时间 | | + +### 用户套餐关系表(UserPackages) + +| 字段名 | 数据类型 | 描述 | 备注 | +| -------------- | --------- | ------------ | ------ | +| Id | int | 主键,自增 | | +| UserId | int | 外键 | 用户ID | +| PackageId | int | 外键 | 套餐ID | +| RemainingCalls | int | 剩余调用次数 | | +| PurchasedAt | timestamp | 购买时间 | | + +> [!CAUTION] +> +> 注:删除用户时需要保证数据一致性。 \ No newline at end of file diff --git a/ErrorCode.md b/ErrorCode.md new file mode 100644 index 0000000..38488a3 --- /dev/null +++ b/ErrorCode.md @@ -0,0 +1,64 @@ +## 错误码定义 + +#### 通用错误码(1xxx) + +| 错误码 | HTTP状态码 | 描述 | Message | +| ------ | ---------- | -------------------- | --------------------- | +| 1000 | 200 | 成功 | Success | +| 1001 | 400 | 参数错误 | Invalid parameters | +| 1002 | 401 | 用户未登录或认证失败 | Unauthorized | +| 1003 | 403 | 无权限访问 | Forbidden | +| 1004 | 404 | 资源未找到 | Resource not found | +| 1005 | 500 | 服务器内部错误 | Internal server error | +| 1006 | 503 | 服务暂时不可用 | Service unavailable | + +#### 用户模块错误码(2xxx) + +| 错误码 | HTTP状态码 | 描述 | Message | +| ------ | ---------- | -------------------- | ----------------------------- | +| 2000 | 200 | 登录成功 | Login successful | +| 2001 | 401 | 用户名或密码错误 | Invalid username or password | +| 2002 | 401 | 用户账户被禁用 | User account is disabled | +| 2003 | 409 | 用户名已存在 | Username already exists | +| 2004 | 404 | 用户不存在 | User not found | +| 2005 | 409 | 邮箱已存在 | Email already exists | +| 2006 | 403 | 用户无权限进行该操作 | Permission denied | +| 2007 | 400 | 密码重置失败 | Password reset failed | +| 2008 | 403 | 凭证到期或无效 | Token expires or is invalid | +| 2009 | 403 | 刷新令牌到期或无效 | Refresh expires or is invalid | + +#### API模块错误码(3xxx) + +| 错误码 | HTTP状态码 | 描述 | Message | +| ------ | ---------- | ------------------------- | ------------------------------------------ | +| 3000 | 200 | API调用成功 | API call successful | +| 3001 | 403 | API访问受限 | API access restricted | +| 3002 | 404 | API不存在 | API not found | +| 3003 | 429 | API调用次数超限 | API call limit exceeded | +| 3004 | 403 | 未购买该API套餐或权限不足 | API package not purchased or access denied | +| 3005 | 500 | API调用失败,服务器错误 | API call failed, server error | + +#### 套餐与支付模块错误码(4xxx) + +| 错误码 | HTTP状态码 | 描述 | Message | +| ------ | ---------- | ------------------ | ------------------------------------ | +| 4000 | 200 | 支付成功 | Payment successful | +| 4001 | 400 | 支付请求无效 | Invalid payment request | +| 4002 | 402 | 支付失败,余额不足 | Payment failed, insufficient balance | +| 4003 | 404 | 订单未找到 | Order not found | +| 4004 | 404 | 套餐未找到 | Package not found | +| 4005 | 409 | 重复支付或订单冲突 | Duplicate payment or order conflict | +| 4006 | 500 | 支付系统错误 | Payment system error | +| 4007 | 200 | 退款成功 | Refund successful | + +#### 日志与系统模块错误码(5xxx) + +| 错误码 | HTTP状态码 | 描述 | Message | +| ------ | ---------- | ------------------ | -------------------------- | +| 5000 | 200 | 日志查询成功 | Log retrieval successful | +| 5001 | 404 | 日志记录未找到 | Log record not found | +| 5002 | 500 | 日志服务异常 | Log service error | +| 5003 | 403 | 无权限查看操作日志 | No permission to view logs | +| 5004 | 500 | 邮件发送错误 | Email send error | +| 5005 | 400 | 验证码错误 | ValidateCode Error | +| 5006 | 404 | 配置不存在 | Config not found | \ No newline at end of file diff --git a/README.md b/README.md index 2352c50..44be4e1 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,87 @@ -# ql_billing_system_backend +## 项目背景 +API管理与用户服务系统是一个集用户管理、API管理、套餐销售与支付管理为一体的综合平台。该系统为用户提供注册、登录、充值和套餐购买等功能,管理员则可以通过系统对API进行统一管理、用户操作日志的记录与查看,以及管理订单与支付情况。整个项目采用了RESTful架构风格,通过简洁的接口设计确保系统的可扩展性、易维护性以及与第三方服务对接的能力。 +## 功能概述 -## Getting started +1. ##### 用户管理 -To make it easy for you to get started with GitLab, here's a list of recommended next steps. + - 登录、注册 + - 用户账号信息管理 + - 用户充值、消费记录查询 -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! +2. ##### API访问管理 -## Add your files + - 用户密钥生成和管理 + - API管理 + - 调用次数和消费金额限制 + - 付费套餐管理 -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +3. ##### 支付模块 -``` -cd existing_repo -git remote add origin http://127.0.0.1:8081/ql/ql_billing_system_backend.git -git branch -M main -git push -uf origin main +4. ##### 订单模块 + + - 订单生成 + - 消费提醒(超额/余额不足) + +5. ##### 系统监控模块 + + - 调用记录 + - 异常告警 + +## 系统依赖 + +- **.NET 6.0 或更高版本** +- **Entity Framework Core** 用于数据库操作 +- **MySQL** 作为后端数据库 +- **Swagger** 用于API文档自动生成 +- **JWT** 实现用户认证和授权 + +## 项目结构 + +```perl +api-billing-system/ +│ +├── API/ # API控制器及路由 +├── Services/ # 业务逻辑层 +├── Models/ # 数据模型定义 +├── Data/ # 数据库上下文及迁移文件 +├── Config/ # 系统配置文件 +├── Tests/ # 单元测试及集成测试 +├── Tools/ # 工具类 +├── README.md # 项目文档(本文件) +├── Database.md # 数据库定义文档 +├── APIDefine.md # API定义文档 +└── ErrorCode.md # 错误码文档 ``` -## Integrate with your tools -- [ ] [Set up project integrations](http://127.0.0.1:8081/ql/ql_billing_system_backend/-/settings/integrations) -## Collaborate with your team +## 模块划分 -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) +1. **API 控制器** -## Test and Deploy +- 负责处理用户请求并返回结果。 +- 提供如用户注册、登录、查询调用次数等API端点。 -Use the built-in continuous integration in GitLab. +1. ##### **业务逻辑层(Services)** -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) +- 负责具体业务逻辑,如套餐管理、消费计算等。 +- 实现支付网关集成。 -*** +1. **数据模型(Models)** -# Editing this README +- 定义数据库中的表结构,如用户表、订单表等。 -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. +1. **数据库(Data)** -## Suggestions for a good README +- 提供数据库连接及操作支持。 +- 包含数据库上下文类及数据库迁移文件。 -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. +## 项目文档列表 -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +- [x] 项目文档 **README.md** +- [x] 数据库文档 **Database.md** +- [x] 错误码文档 **ErrorCode.md** +- [x] API定义文档 **APIDefine.md** +- [ ] API文档 **[API文档](https://apifox.com/apidoc/shared-8730c2eb-595b-4cf4-941d-d58914d4cd1e)** \ No newline at end of file